Every API failure returned by the TricklePay backend includes a stable, machine-readable code alongside a human-readable message. This allows client developers to build robust error handling and branch on error categories without having to parse human-readable text.
Error responses are returned as a JSON object with the following structure:
{
"code": "NOT_FOUND",
"error": "Route GET /missing-path not found",
"requestId": "client-trace-42"
}code: The stable error code (see below).error: A human-readable error message. ForINTERNAL_SERVER_ERROR, raw error details (stack traces, SQL fragments) are redacted and a generic"internal server error"message is returned instead.requestId: The request ID associated with the error, useful for correlating client errors with server-side logs.
The table below lists every HTTP status code the service can return and the code it maps to.
| HTTP Status | Code | When it occurs |
|---|---|---|
400 |
VALIDATION_ERROR |
Invalid or malformed request — missing required fields, bad parameter values, repeated query parameters, or a query string that exceeds the configured length limit. |
404 |
NOT_FOUND |
The requested resource or route cannot be found. |
413 |
REQUEST_ERROR |
The request body exceeds the configured BODY_LIMIT (default 1 MiB). |
429 |
RATE_LIMITED |
The client has exceeded the configured request rate limit. The response includes a Retry-After header with the number of seconds until the window resets. Clients should back off and retry after that interval. |
500 |
INTERNAL_SERVER_ERROR |
An unexpected server-side error. The original error is logged internally with the request ID; the client receives a safe, redacted message. |
curl -s http://localhost:3000/streams/invalid | jq{
"code": "VALIDATION_ERROR",
"error": "invalid stream id",
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}curl -s http://localhost:3000/streams/99999999 | jq{
"code": "NOT_FOUND",
"error": "stream not found",
"requestId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}curl -s -D - http://localhost:3000/streams | head -20HTTP/1.1 429 Too Many Requests
Retry-After: 42
x-request-id: c3d4e5f6-a7b8-9012-cdef-123456789012
{
"code": "RATE_LIMITED",
"error": "rate limit exceeded",
"requestId": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}The Retry-After value is the number of seconds remaining in the current rate-limit window. Wait that long before retrying rather than branching on the HTTP status code alone.
Error messages sent to clients are redacted to prevent leaking sensitive information such as database connection strings, internal hostnames, or stack traces. The full, unredacted error remains in the server log for operator diagnosis.
The following information is stripped from error messages before they reach the client:
- Credential-bearing URLs: Any URL containing embedded credentials (e.g.,
postgres://user:password@host/db) is replaced with[redacted]. - Multi-line errors: Stack traces and multi-line error messages are collapsed to their first line only. Subsequent lines (which often contain file paths, function names, and internal implementation details) are discarded.
- Internal server errors: When the HTTP status is 500 or above, the client receives the generic message
"internal server error"regardless of the original error text.
The original, unredacted error — including the full stack trace, connection string, SQL fragment, or any other diagnostic detail — is preserved in the structured server log. Every error log entry includes the reqId field, which matches the requestId returned to the client. Use this request ID to correlate a client-reported failure with its detailed server-side log entry.
Original error (server-side):
Invalid prisma.$queryRaw invocation:
Connection failed: postgres://tricklepay:s3cret@db.internal:5432/tricklepay
at TCP.connect (node:net:123:45)
at Database.query (/app/db.ts:67:12)
Redacted message (client response):
{
"code": "INTERNAL_SERVER_ERROR",
"error": "internal server error",
"requestId": "req-abc123"
}Server log entry (for operator diagnosis):
{
"level": 50,
"time": 1234567890123,
"reqId": "req-abc123",
"err": {
"type": "Error",
"message": "Invalid prisma.$queryRaw invocation: Connection failed: postgres://tricklepay:s3cret@db.internal:5432/tricklepay",
"stack": "Error: Invalid prisma.$queryRaw invocation...\n at TCP.connect (node:net:123:45)\n at Database.query (/app/db.ts:67:12)"
},
"msg": "request failed"
}Exposing raw database or RPC errors to clients can leak:
- Database credentials embedded in connection strings
- Internal hostnames and network topology
- Software versions and library paths from stack traces
- SQL query structure and table names
By redacting client responses while preserving full details server-side, operators retain the diagnostic information needed to fix failures without exposing sensitive infrastructure details to external callers.