{"record":{"id":"40ecd7d0240c074c","repo":"can1357/oh-my-pi","slug":"the-upload-response-did-not-include-a-direct-image","errorCode":null,"errorMessage":"the upload response did not include a direct image URL","messagePattern":"the upload response did not include a direct image URL","errorType":"exception","errorClass":"LegacyDestinationError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-legacy.ts","lineNumber":141,"sourceCode":"\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}\n\nfunction optionalBasicHeaders(\n\tdestination: BlobDestinationId,\n\tconfig: DestinationRuntimeConfig,\n\tusernameKey: string,\n\tpasswordKey: string,\n): Headers | undefined {\n\tconst username = credentialString(config, usernameKey);\n\tconst password = credentialString(config, passwordKey);\n\tif (!username && !password) return undefined;\n\tif (!username || !password) {\n\t\tthrow new LegacyDestinationError(","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-legacy.ts#L123-L159","documentation":"This LegacyDestinationError is thrown by directJsonUrl() when the parsed JSON response object (including a nested `response` object) contains none of the recognized direct-URL keys: `direct_url`, `directUrl`, `url`, or `URL`. The upload 'succeeded' but the library cannot locate the link to the stored file.","triggerScenarios":"The endpoint replies with valid JSON lacking any URL field — e.g. `{\"status\":\"ok\",\"id\":\"abc\"}`, or uses a key name outside the four recognized aliases; thrown before httpUrl() validation, called from url() and upload().","commonSituations":"Custom replacement endpoints with their own response schema (e.g. `download_url` or `link` keys); APIs that return only an identifier requiring a second request to fetch the URL; version drift where the service renamed its response fields.","solutions":["Configure or patch the endpoint to include a recognized key (`url` or `direct_url`) in its JSON response","Add a server-side response shim that maps the service's actual key (e.g. `download_url`) to `url`","If the API returns only an ID, proxy it and construct the absolute URL server-side","Compare the actual response keys with the accepted aliases: direct_url, directUrl, url, URL (top-level or under `response`)"],"exampleFix":"// before (server response)\n{\"status\":\"ok\",\"download_url\":\"https://cdn.example.com/f.png\"}\n// after\n{\"status\":\"ok\",\"url\":\"https://cdn.example.com/f.png\"}","handlingStrategy":"validation","validationCode":"const record = await response.json();\nconst nested = record?.response ?? {};\nconst hasUrl = [\"direct_url\",\"directUrl\",\"url\",\"URL\"]\n  .some(k => typeof record?.[k] === \"string\" || typeof nested?.[k] === \"string\");\nif (!hasUrl) throw new Error(\"endpoint response lacks a direct URL field\");","typeGuard":"function hasDirectUrl(record: unknown): record is Record<string, unknown> & { url: string } {\n  const keys = [\"direct_url\", \"directUrl\", \"url\", \"URL\"];\n  const r = record as Record<string, unknown>;\n  const nested = (r?.response ?? {}) as Record<string, unknown>;\n  return keys.some(k => typeof r?.[k] === \"string\" || typeof nested?.[k] === \"string\");\n}","tryCatchPattern":"try {\n  const result = await uploader.upload(request);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"did not include a direct image URL\")) {\n    // map the endpoint's actual field name to `url`/`direct_url`\n  } else throw err;\n}","preventionTips":["Include a `url` or `direct_url` key in custom endpoint responses","Map vendor-specific keys (download_url, link) to the recognized aliases server-side","Keep an integration test asserting the response carries a direct URL key","Document the four accepted key names for endpoint implementers"],"tags":["network","upload","response-parsing","json"],"backgroundTag":"missing-response-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}