{"record":{"id":"3d25c60c6e4735c3","repo":"can1357/oh-my-pi","slug":"the-upload-response-did-not-contain-a-valid-direct","errorCode":null,"errorMessage":"the upload response did not contain a valid direct URL","messagePattern":"the upload response did not contain a valid direct URL","errorType":"exception","errorClass":"LegacyDestinationError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-legacy.ts","lineNumber":100,"sourceCode":"\t\t}\n\t}\n\treturn endpoint;\n}\n\nfunction sendSpaceEndpoint(config: DestinationRuntimeConfig): URL {\n\tconst endpoint = configuredEndpoint(\"sendspace\", config);\n\tif (endpoint.hostname.toLowerCase() === SENDSPACE_DEFAULT_HOST) {\n\t\tthrow new DestinationUnavailableError(\"sendspace\", \"the deprecated public discovery endpoint cannot be used\");\n\t}\n\treturn endpoint;\n}\n\nfunction httpUrl(destination: BlobDestinationId, raw: string, base?: URL): string {\n\tlet url: URL;\n\ttry {\n\t\turl = base ? new URL(raw, base) : new URL(raw);\n\t} catch (error) {\n\t\tthrow new LegacyDestinationError(destination, \"the upload response did not contain a valid direct URL\", error);\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new LegacyDestinationError(destination, \"the upload response URL must use HTTP or HTTPS\");\n\t}\n\treturn url.href;\n}\n\nfunction objectValue(value: unknown): Readonly<Record<string, unknown>> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n\treturn value as Readonly<Record<string, unknown>>;\n}\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;","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-legacy.ts#L82-L118","documentation":"This LegacyDestinationError is thrown by httpUrl() when a URL string extracted from an upload endpoint's HTTP response cannot be parsed by `new URL(raw)` (optionally resolved against the request's base endpoint URL). It indicates the remote service returned a malformed 'direct URL' field, and the underlying parse failure is attached as `cause`.","triggerScenarios":"An upload completes and the response JSON contains a `direct_url`/`directUrl`/`url`/`URL` field whose value is not a parseable URL — e.g. a relative path like '/files/abc.png' when no base resolution works, empty string that slipped past firstString, HTML snippet, or a truncated URL from a misbehaving proxy; called from directJsonUrl, url(), upload(), and discoverSendSpaceNode.","commonSituations":"Uploading to a self-hosted replacement endpoint that returns relative paths instead of absolute URLs; a reverse proxy injecting an error page whose extracted string is not a URL; service changed its response schema.","solutions":["Inspect the raw HTTP response (log it or use a proxy) to see what the endpoint actually returned in the URL field","Fix or replace the custom endpoint so it returns absolute http(s) URLs","If the endpoint returns relative paths, front it with a proxy that rewrites them to absolute URLs","Check the error's `cause` for the exact URL parse failure to identify the offending string"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAbsoluteHttpUrl(value: unknown): value is string {\n  if (typeof value !== \"string\") return false;\n  try {\n    const u = new URL(value);\n    return u.protocol === \"https:\" || u.protocol === \"http:\";\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  const result = await uploader.upload(request);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"did not contain a valid direct URL\")) {\n    console.error(\"cause:\", err.cause); // inspect what the server returned\n  } else throw err;\n}","preventionTips":["Test your replacement endpoint once with curl and inspect the response URL field","Ensure the endpoint returns absolute http(s) URLs, never relative paths","Log raw response bodies during integration of custom endpoints","Validate third-party endpoint responses against the expected schema before rollout"],"tags":["network","upload","url-parsing","response-parsing"],"backgroundTag":"malformed-upload-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}