Error Handling and Avoiding Information Leakage in Clojure

Learn how to keep Clojure error handling useful for operators without leaking secrets, internal topology, or sensitive payloads to users, logs, and support tools.

Information leakage: Accidental disclosure of secrets, internal details, or sensitive context through error responses, logs, stack traces, or support tooling.

Security problems often hide inside “helpful” failure handling. The application works correctly on the happy path, but the first exception prints a SQL fragment, token, hostname, file path, or customer payload into a place it never should have appeared.

Good error handling separates three audiences:

  • the user or API caller
  • the operator or developer investigating the failure
  • the security or audit process reviewing what happened later

Each audience needs different information.

User-Facing Errors Should Be Useful but Narrow

Users need:

  • the outcome
  • whether retry makes sense
  • what correction is required
  • a correlation or request ID when support must investigate

Users usually do not need:

  • stack traces
  • raw exception messages
  • SQL text
  • internal service names
  • filesystem paths
  • secrets or tokens

That means good error responses are intentionally smaller than the internal error context.

Keep Rich Failure Detail in Structured Logs, Not Responses

Detailed diagnostics still matter. They just belong in controlled logs or traces:

 1(ns myapp.error
 2  (:require [clojure.tools.logging :as log]))
 3
 4(defn internal-error-response [request ex]
 5  (let [request-id (:request/id request)]
 6    (log/error ex {:request/id request-id
 7                   :route (:uri request)
 8                   :user/id (get-in request [:identity :user/id])}
 9      "request failed")
10    {:status 500
11     :body {:error :internal-error
12            :request/id request-id}}))

This gives operators a useful join key without teaching the caller how the system failed internally.

Exceptions Should Carry Context, but Context Should Be Reviewable

ex-info is useful because it lets you attach structured data. The security question is which data belongs there.

Safer examples:

  • operation name
  • entity ID
  • request ID
  • validation category
  • integration name

Risky examples:

  • full credentials
  • full tokens
  • raw personal payloads
  • plaintext secrets

If exception data might end up in logs or support traces, treat it as a semi-public channel inside the organization and keep it accordingly disciplined.

Redaction Rules Need to Apply on Failure Paths Too

Many teams carefully redact normal logging and then forget exception handling. Review:

  • middleware-level exception logs
  • async worker failure logs
  • dead-letter payloads
  • alert payloads
  • third-party error reporting tools

If those systems receive the full request or environment by default, error handling can become a data-exposure vector.

Different Failure Classes Deserve Different Responses

Do not flatten every problem into HTTP 500:

  • validation failures are not internal errors
  • authorization denials are not internal errors
  • missing resources are not internal errors
  • dependency timeouts may deserve a retryable signal

That is better for correctness and better for security because callers do not need the internal details to know how to respond.

Security Testing Should Include Failure Surfaces

Review and test:

  • does a bad token leak parsing detail?
  • does a failed DB call reveal the connection string?
  • does a missing file expose server paths?
  • does an exception alert include secrets?

A secure system must be careful about what it says when things go wrong.

Common Failure Modes

Returning Raw Exception Messages to Clients

This is the shortest path from an internal detail to an attacker hint.

Logging Full Request Bodies by Default

Useful during one incident, damaging forever after.

Emitting Different Errors That Reveal Hidden State

For example, login or reset flows that distinguish too clearly between “user exists” and “user does not exist.”

Forgetting Async and Background Error Channels

Workers, queue consumers, and scheduled jobs can leak just as much as HTTP handlers.

Practical Heuristics

Keep client-facing error responses small, structured, and action-oriented. Put richer diagnostics into controlled logs with request IDs and carefully chosen metadata. Redact failure paths as aggressively as success paths, and review exception data as if it may eventually be copied into alerts or support tooling. In Clojure, explicit maps make this discipline easier. Use that explicitness to decide exactly what the caller sees and exactly what the operator keeps.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026