hcengineering/platform · error · ApiError
err.code
err.code
Error message
err.message
What it means
The pod-notification error-handling middleware converts a thrown ApiError into HTTP 400 { code: err.code, message: err.message }. The message in the response is the ApiError's own message. This indicates the server deemed the request invalid — a client-fixable problem — as opposed to the anonymous 500 fallback for non-ApiError exceptions.
Source
Thrown at services/notification/pod-notification/src/server.ts:52
app.use(cors())
app.use(express.json())
endpoints.forEach((endpoint) => {
if (endpoint.type === 'get') {
app.get(endpoint.endpoint, catchError(endpoint.handler))
} else if (endpoint.type === 'post') {
app.post(endpoint.endpoint, catchError(endpoint.handler))
}
})
app.use((_req, res, _next) => {
res.status(404).send({ message: 'Not found' })
})
app.use((err: any, _req: any, res: any, _next: any) => {
if (err instanceof ApiError) {
res.status(400).send({ code: err.code, message: err.message })
return
}
res.status(500).send({ message: err.message })
})
return app
}
export function listen (e: Express, port: number, host?: string, onListening?: () => void): Server {
const cb = (): void => {
if (onListening !== undefined) {
onListening()
} else {
console.log(`Notification service has been started at ${host ?? '*'}:${port}`)
}
}
View on GitHub (pinned to 63e28dc964)
Solutions
- Inspect the response body's 'code' field — it pinpoints the failing validation; adjust the request accordingly.
- Validate each subscription (endpoint URL, keys.p256dh, keys.auth) client-side before submitting the batch.
- Update stored subscriptions: remove ones whose codes indicate permanent rejection (e.g. 410 gone upstream).
- In handler code you own, throw ApiError with specific codes rather than raw Errors so clients get actionable 400s.
Example fix
// before
if (!res.ok) throw new Error(`request failed: ${res.status}`)
// after
const body = await res.json()
if (!res.ok) throw new Error(`[${body.code ?? res.status}] ${body.message}`) Defensive patterns
Strategy: try-catch
Validate before calling
function validatePushPayload(body: unknown): string[] {
const errs: string[] = []
if (typeof (body as any)?.data !== 'object') errs.push('data missing')
if (!Array.isArray((body as any)?.subscriptions)) errs.push('subscriptions missing')
return errs
} Type guard
function isApiErrorBody(v: unknown): v is { code: string; message: string } {
return v !== null && typeof v === 'object' && 'code' in v && 'message' in v
} Try / catch
const res = await fetch(pushUrl, opts)
if (!res.ok && res.status !== 404) {
const body = await res.json()
if (isApiErrorBody(body)) throw new Error(`Push ApiError ${body.code}: ${body.message}`)
} Prevention
- Validate subscriptions and data against the schema before calling
- Handle the 'code' field explicitly in client error paths
- Remove permanently rejected subscriptions based on returned codes
When it happens
Trigger: Any notification endpoint handler (wrapped by catchError) throws ApiError — e.g. push payload validation failures inside the handler, invalid subscription entries that fail domain checks, or web-push library errors deliberately re-thrown as ApiError.
Common situations: Clients sending PushSubscription objects with missing/invalid keys that deeper validation rejects; expired push endpoints rejected by upstream causing the handler to throw ApiError; generic SDK handling that ignores the 'code' field and reports only the message.
Related errors
- err.code
- 'data' is missing
- 'subscriptions' is missing
- Failed to load server config
- getDisplayMedia not supported
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/6f23f501cef52e78.
Report an issue: GitHub.