hcengineering/platform · warning · ApiError
err.code
err.code
Error message
err.message (500 fallback sends err object when message is empty)
What it means
The analytics-collector server's global error handler behaves identically to the ai-bot one: ApiError instances are sent with their code/message, everything else gets a 500 with {message: err.message} or the raw error object when the message is empty. A response containing the whole error object indicates a non-ApiError with an empty message escaped from a route handler.
Source
Thrown at services/analytics-collector/pod-analytics-collector/src/server.ts:289
if (evt.event === AnalyticEventType.Error) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { error_message, error_type, error_stack } = evt.properties ?? {}
reportOTELError({ message: error_message ?? 'Unknown error', stack: error_stack, name: error_type ?? '' })
} else {
reportOTEL('info', evt.event, evt.timestamp, { ...evt.properties, distinct_id: evt.distinct_id })
}
}
})
)
app.use((err: any, _req: any, res: any, _next: any) => {
console.log(err)
if (err instanceof ApiError) {
res.status(err.code).send({ code: err.code, message: err.message })
return
}
res.status(500).send(err.message?.length > 0 ? { message: err.message } : err)
})
return app
}
async function sendEventsToPosthog (events: AnalyticEvent[], req: Request): Promise<void> {
const posthogEvents: Record<string, any>[] = []
for (const evt of events) {
posthogEvents.push(await preparePostHogEvent(evt, req))
}
const payload = {
api_key: config.PostHogAPI,
batch: posthogEvents.reverse()
}
const posthogPayloadSize = JSON.stringify(payload).length
console.log(`Sending to PostHog: ${posthogEvents.length} events, ${posthogPayloadSize} bytes`)View on GitHub (pinned to 63e28dc964)
Solutions
- Inspect the raw error object returned in the 500 response to find the root cause.
- Convert expected failures (validation, upstream errors) into ApiError with a proper code and message.
- Ensure all async handlers await promises so rejections carry messages.
- Add logging/middleware to normalize thrown non-Error values into Errors.
Example fix
// before
sendEventsToPosthog(events, req) // floating promise
// after
try {
await sendEventsToPosthog(events, req)
} catch (e) {
throw new ApiError(502, `posthog delivery failed: ${String(e)}`)
} Defensive patterns
Strategy: try-catch
Validate before calling
function extractServerMessage(body: unknown): string {
if (typeof body === 'object' && body !== null && 'message' in body && typeof (body as any).message === 'string') return (body as any).message
return 'unstructured analytics-collector error: ' + JSON.stringify(body)
} Type guard
function isApiErrorPayload(x: unknown): x is { code: number; message: string } {
return typeof x === 'object' && x !== null && typeof (x as any).code === 'number' && typeof (x as any).message === 'string'
} Try / catch
app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => {
if (err instanceof ApiError) {
return res.status(err.code).json({ code: err.code, message: err.message })
}
console.log(err)
const message = err instanceof Error && err.message.length > 0 ? err.message : 'internal error'
return res.status(500).json({ message })
}) Prevention
- Wrap Posthog/upstream delivery failures in ApiError with descriptive messages.
- Validate event payloads before ingestion and throw typed errors.
- Never throw non-Error values; log-and-convert unknown throws centrally.
- Add tests covering the 500 fallback to ensure responses stay structured.
When it happens
Trigger: Unhandled exceptions in analytics ingestion routes that are not ApiError and carry an empty/undefined message — failed event validation libraries throwing bare errors, message-less rejections from sendEventsToPosthog, or thrown non-Error values.
Common situations: Posthog/network calls failing with opaque errors; schema validation throwing message-less errors; bugs throwing plain objects; missing await causing rejected promises to hit the error middleware.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/fe99a59edc53ed99.
Report an issue: GitHub.