{"record":{"id":"3e0c94d35f66650d","repo":"can1357/oh-my-pi","slug":"the-upload-endpoint-returned-invalid-json","errorCode":null,"errorMessage":"the upload endpoint returned invalid JSON","messagePattern":"the upload endpoint returned invalid JSON","errorType":"exception","errorClass":"LegacyDestinationError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-legacy.ts","lineNumber":129,"sourceCode":"}\n\nfunction firstString(record: Readonly<Record<string, unknown>>, keys: readonly string[]): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) return value.trim();\n\t}\n\treturn undefined;\n}\n\nasync function jsonObject(\n\tdestination: BlobDestinationId,\n\tresponse: Response,\n): Promise<Readonly<Record<string, unknown>>> {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = await response.json();\n\t} catch (error) {\n\t\tthrow new LegacyDestinationError(destination, \"the upload endpoint returned invalid JSON\", error);\n\t}\n\tconst record = objectValue(value);\n\tfor (const _ in record) return record;\n\tthrow new LegacyDestinationError(destination, \"the upload endpoint returned an invalid JSON object\");\n}\n\nfunction directJsonUrl(destination: BlobDestinationId, record: Readonly<Record<string, unknown>>, base: URL): string {\n\tconst nested = objectValue(record.response);\n\tconst raw =\n\t\tfirstString(record, [\"direct_url\", \"directUrl\", \"url\", \"URL\"]) ??\n\t\tfirstString(nested, [\"direct_url\", \"directUrl\", \"url\", \"URL\"]);\n\tif (!raw) throw new LegacyDestinationError(destination, \"the upload response did not include a direct image URL\");\n\treturn httpUrl(destination, raw, base);\n}\n\nfunction basicAuthorization(username: string, password: string): string {\n\treturn `Basic ${Buffer.from(`${username}:${password}`, \"utf8\").toString(\"base64\")}`;\n}","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-legacy.ts#L111-L147","documentation":"This LegacyDestinationError is thrown by jsonObject() when `response.json()` fails — i.e. the upload endpoint's HTTP response body is not valid JSON (or the body was already consumed). The library expects every legacy JSON-based uploader response to parse as JSON, and wraps the SyntaxError as `cause`.","triggerScenarios":"The endpoint returns HTML (error page, login page, Cloudflare challenge), an empty body, plain text, or truncated JSON; a 5xx/403 page is returned with text/html content-type; the response stream was interrupted mid-body; called from the `data` helper on JSON-based uploaders.","commonSituations":"Self-hosted replacement endpoint returning its framework's HTML 404/500 page; auth failure redirecting to an HTML login page; rate-limit/CDN block pages; proxy returning non-JSON errors.","solutions":["Log the raw response body and status before parsing to see what the server actually sent","Fix authentication so the endpoint stops returning HTML error/login pages","Ensure the custom endpoint always replies with a JSON object (even on errors)","Check for CDN/WAF challenge pages (Cloudflare) and allowlist the client"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function looksLikeJson(response: Response): boolean {\n  const ct = response.headers.get(\"content-type\") ?? \"\";\n  return ct.includes(\"application/json\");\n}","tryCatchPattern":"try {\n  const result = await uploader.upload(request);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"returned invalid JSON\")) {\n    console.error(\"cause:\", err.cause); // SyntaxError details + check status/body\n  } else throw err;\n}","preventionTips":["Check response status and content-type before assuming JSON","Watch for HTML error/login/Cloudflare-challenge pages from misconfigured endpoints","Ensure custom endpoints always emit a JSON body even on errors","Test endpoints under failure conditions (403, 500) to see what body they produce"],"tags":["network","upload","json","response-parsing"],"backgroundTag":"invalid-json-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}