An error response is an output interface. Returning an exception's string can reveal a database path, SQL fragment, access token or internal identifier that the caller did not need. A safe public failure contract names what the caller can act on without reproducing arbitrary diagnostics. The service should still leave operators enough controlled information to correlate and investigate failures.
This lab returns a status number, a small public dictionary and a JSON audit line. It sends no HTTP response and writes no log file. The status numbers model an application contract; actual headers, caching, framework exception handlers and log access controls are outside the fixture.
The supported failure kinds are bad_request, not_found, forbidden and internal. Each maps to a fixed status and public message. An unknown kind becomes an internal failure rather than echoing the unknown text. A correlation ID is a separate, constrained string. In production its source and uniqueness need a defined policy: the helper's syntax check alone does not make a client-proposed correlation ID authoritative.
The public body contains exactly error and request_id. The audit event contains exactly the canonical kind, status, request ID, normalized action and actor ID. The context mapping may also contain an exception, password, cookie or deeply nested request body. Those fields are deliberately omitted. Building a small allowlist is safer than copying a request and trying to remember every current and future secret field to remove.
import json
line = json.dumps({"action": "edit\nadmin"}, sort_keys=True)
print("\n" in line)
print(json.loads(line)["action"] == "edit\nadmin")The first result is false: JSON represents the newline inside a string instead of inserting a new physical log record. The second is true: a parser recovers the original value. Structured serialization handles representation; it does not decide whether retaining a value is appropriate. Our actual audit schema accepts only read, update and upload as actions, replacing other values with unknown, so it retains even less untrusted text.
Actor IDs must have exact type int and be positive; booleans do not count as IDs. Invalid actor data becomes None, never a forged integer coerced from arbitrary text. Invalid correlation IDs raise ValueError before any output is produced. The fixture normalizes these fields without mutating the context mapping.
A generic error message is not a reason to suppress all diagnostics. Real systems need carefully protected operational details, controlled access, retention limits and a plan for logging failures. Tokens, passwords and sensitive personal data should not be dumped into either sink. Exception traces may require a separate protected channel with redaction; this course does not build that channel.
Test secrecy as an observable property: place a unique fake secret in several context fields, inspect both returned representations and assert that it is absent. Also test whether the event schema can be overwritten through context keys. The bug hunts exercise these exact mistakes. Passing those tests supports this helper's information contract, not a claim that every handler, proxy and monitoring agent in an application excludes sensitive data.