diagnostic errors

why

errors are the product's main channel for telling humans and machines an invariant failed. the invariant protected: the channel stays typed, stable, actionable, and safe. swallowing an error converts failure into a false business fact — "no results" instead of "the dependency is down" — misleading users, support, and product decisions at once, and hiding the failure from whoever could fix it. typed diagnostics at the narrowest proving boundary keep the repair loop closed while exposing nothing private.

rule

fail fast at the narrowest boundary that can prove an invariant false. errors crossing runtime or API boundaries are typed, stable, actionable, and safe for humans and machines. swallowed errors turn failures into false absence: a dependency times out, the code returns empty, the user sees "no results yet" and believes their data is missing — workarounds, support tickets, churn — while the team never learns the dependency was down. the false absence cascades into product decisions on incomplete information until a customer reports it as data loss.

required diagnostics

a diagnostic must carry:

  • safe user message
  • stable code
  • operation or correlation ID
  • structured context
  • original cause in protected logs
  • retryability
  • correct remediation class

choose the failure class at the proving boundary

observed failureproving boundarycaller action
malformed request or impossible inputrequest/schema parsercorrect the input; do not retry unchanged.
missing identity or permissionauthorization boundaryauthenticate, request access, or stop.
missing configuration or unsupported setupintegration/configuration boundarycorrect a prerequisite.
timeout or dependency outagedependency adapterretry only when the operation is safe.
violated durable invariantdomain/persistence boundarystop and investigate data or implementation.

the same problem must not acquire a different public code merely because it crosses another layer. preserve the causal chain in protected logs; keep the public contract stable enough for the interface and repair loop to classify.

decision process

shortcutwhy it failsselected direction
catch and return an empty valueturns a failure into a false business factfail at the narrowest proving boundary.
send raw provider text to the interfaceexposes internals without a valid recovery pathmap technical causes to a typed, safe diagnostic category.
use one generic error codeforces every caller to guess retry versus correctionencode stable remediation class and retryability.
log only a message stringloses the operation, resource, and causal chain needed for repairretain structured context and the protected original cause.

verification

force one representative failure per class. confirm the response gives a safe message, stable code, correlation identifier, and valid action; confirm protected logs connect that identifier to the original cause without exposing secrets to the caller.

do not

  • swallow errors, return empty success, or hide a failure behind a fallback.
  • expose secrets or unnecessary private data.
  • replace a precise failure with "something went wrong."

good and bad

bad

try {
return await readWorkflowRun(input);
} catch {
return null;
}

an unavailable dependency becomes a false absence with no repair evidence: the dependency times out, the code returns null, the UI renders "no results yet." the user refreshes, checks whether their workflow ran, contacts support. support checks the database and finds the workflow did complete — the results-loading dependency was down for 3 hours. the user loses confidence (looks like data loss), tickets pile up, and the team never learns the dependency failed. the problem repeats tomorrow.

good

throw new DependencyUnavailableDiagnostic({
operationId,
retryable: true,
cause,
});

the boundary reports a stable, safe retry category; protected logs retain the original cause. the interface can honestly offer "retry" instead of a dead-end string, support classifies the ticket from the stable code and correlation id, on-call reaches the causal chain without the customer seeing internals. retry policies, alerting, and product decisions key off the typed class — the same outage stops being rediscovered.

search pages

go to any page