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:
Each audience needs different information.
Users need:
Users usually do not need:
That means good error responses are intentionally smaller than the internal error context.
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.
ex-info is useful because it lets you attach structured data. The security question is which data belongs there.
Safer examples:
Risky examples:
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.
Many teams carefully redact normal logging and then forget exception handling. Review:
If those systems receive the full request or environment by default, error handling can become a data-exposure vector.
Do not flatten every problem into HTTP 500:
That is better for correctness and better for security because callers do not need the internal details to know how to respond.
Review and test:
A secure system must be careful about what it says when things go wrong.
This is the shortest path from an internal detail to an attacker hint.
Useful during one incident, damaging forever after.
For example, login or reset flows that distinguish too clearly between “user exists” and “user does not exist.”
Workers, queue consumers, and scheduled jobs can leak just as much as HTTP handlers.
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.