{"record":{"id":"caa9a2e2e8bcbfef","repo":"thedotmack/claude-mem","slug":"message-baseroutehandler","errorCode":null,"errorMessage":"message","messagePattern":"message","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/services/worker/http/BaseRouteHandler.ts","lineNumber":77,"sourceCode":"      ?? req.get?.('x-claude-mem-platform-source');\n    return BaseRouteHandler.firstString(req.query.platformSource)\n      ?? BaseRouteHandler.firstString(req.query.platform_source)\n      ?? BaseRouteHandler.firstString(body.platformSource)\n      ?? BaseRouteHandler.firstString(body.platform_source)\n      ?? BaseRouteHandler.firstString(header);\n  }\n\n  protected getPlatformSourceFromRequest(req: Request): string {\n    return normalizePlatformSource(BaseRouteHandler.rawPlatformSourceFromRequest(req));\n  }\n\n  protected getOptionalPlatformSourceFromRequest(req: Request): string | undefined {\n    const rawPlatformSource = BaseRouteHandler.rawPlatformSourceFromRequest(req);\n    return rawPlatformSource ? normalizePlatformSource(rawPlatformSource) : undefined;\n  }\n\n  protected badRequest(res: Response, message: string): void {\n    res.status(400).json({ error: message });\n  }\n\n  protected notFound(res: Response, message: string): void {\n    res.status(404).json({ error: message });\n  }\n\n  protected handleError(res: Response, error: Error, context?: string): void {\n    const statusCode = error instanceof AppError ? error.statusCode : 500;\n    // Client errors (4xx AppErrors) are routine bad input, not server faults, so\n    // they log at WARN and are NOT routed to the error sink — surfacing a\n    // validation rejection like a bad corpus name as a captured $exception just\n    // pollutes error tracking with noise. Only true server faults (5xx, or any\n    // non-AppError, which maps to 500) go through logger.failure, whose Error\n    // payload routes through logger.error → the error sink → captureException\n    // (Phase 3): a REDACTED $exception to PostHog Error Tracking, consent-gated,\n    // kill-switch-gated, and rate-limited.\n    const isClientError = statusCode >= 400 && statusCode < 500;\n    if (isClientError) {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/worker/http/BaseRouteHandler.ts#L59-L95","documentation":"badRequest is BaseRouteHandler's helper that responds 400 with `{ error: message }`. The thrown 'message' here is the helper's parameter — the concrete text comes from callers like parseIntParam when a required numeric/typed query param fails to parse.","triggerScenarios":"Calling a worker API route with a query parameter that parseIntParam cannot parse (non-numeric where an int is required, empty value).","commonSituations":"Passing 'abc' or '' as ?limit= or ?id=; URL-encoded garbage in pagination params; SDK/client sending string types where ints are expected.","solutions":["Send a valid integer for the parameter (e.g. ?limit=50)","Inspect the route's parseIntParam call to see the expected param name and constraints","Omit optional params entirely instead of sending empty values"],"exampleFix":"// before\nGET /api/observations?limit=abc\n// after\nGET /api/observations?limit=50","handlingStrategy":"validation","validationCode":"function isValidIntParam(v: unknown): v is number {\n  const n = Number(v);\n  return Number.isInteger(n) && n > 0;\n}\n// before sending: if (!isValidIntParam(limit)) omit param;","typeGuard":"function isPositiveInt(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":null,"preventionTips":["Validate all query params client-side before requests","Never send empty strings for optional numeric params","Use a typed client wrapper that enforces int params"],"tags":["http","validation","query-params"],"backgroundTag":"invalid-query-parameter","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}