{"record":{"id":"80655143e62d94b8","repo":"can1357/oh-my-pi","slug":"a-user-supplied-replacement-endpoint-is-required","errorCode":null,"errorMessage":"a user-supplied replacement endpoint is required","messagePattern":"a user-supplied replacement endpoint is required","errorType":"validation","errorClass":"DestinationUnavailableError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-legacy.ts","lineNumber":64,"sourceCode":"}\n\ninterface SendSpaceNode {\n\treadonly url: URL;\n\treadonly maxFileSize: string;\n\treadonly uploadIdentifier: string;\n\treadonly extraInfo: string;\n}\n\nfunction failure(destination: BlobDestinationId, error: unknown): Error {\n\tif (error instanceof DestinationUnavailableError || error instanceof LegacyDestinationError) return error;\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn new LegacyDestinationError(destination, message, error);\n}\n\nfunction configuredEndpoint(destination: BlobDestinationId, config: DestinationRuntimeConfig): URL {\n\tconst raw = optionString(config, \"endpoint\")?.trim();\n\tif (!raw) {\n\t\tthrow new DestinationUnavailableError(destination, \"a user-supplied replacement endpoint is required\");\n\t}\n\tlet endpoint: URL;\n\ttry {\n\t\tendpoint = new URL(raw);\n\t} catch (error) {\n\t\tthrow new LegacyDestinationError(destination, \"the configured endpoint is not a valid URL\", error);\n\t}\n\tif (endpoint.protocol !== \"https:\" && endpoint.protocol !== \"http:\") {\n\t\tthrow new LegacyDestinationError(destination, \"the configured endpoint must use HTTP or HTTPS\");\n\t}\n\tconst hostname = endpoint.hostname.toLowerCase();\n\tconst blocked = BLOCKED_CUSTOM_HOSTS[destination];\n\tif (blocked) {\n\t\tfor (const domain of blocked) {\n\t\t\tif (hostname === domain || hostname.endsWith(`.${domain}`)) {\n\t\t\t\tthrow new DestinationUnavailableError(destination, \"the defunct public endpoint cannot be used\");\n\t\t\t}\n\t\t}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-legacy.ts#L46-L82","documentation":"configuredEndpoint() throws a DestinationUnavailableError when a legacy destination has no user-supplied 'endpoint' option. Legacy image hosts whose original APIs died can only be used if you point them at a replacement (e.g. a self-hosted clone), so the endpoint is mandatory.","triggerScenarios":"Creating a legacy uploader (uploaders-legacy.ts) from a config that omits or has a blank 'endpoint' option — the trim() leaves an empty string and the guard fires.","commonSituations":"Configuring a legacy host by name only, assuming built-in defaults exist, or the endpoint option key being misspelled/empty after config parsing.","solutions":["Add an endpoint option with the replacement host URL, e.g. { \"endpoint\": \"https://my-legacy-clone.example\" }","Check the option key spelling and that the value is non-empty after trimming","Pick a non-legacy destination type if you have no replacement endpoint to supply"],"exampleFix":"// before\n{ \"kind\": \"legacy-host\", \"options\": {} }\n// after\n{ \"kind\": \"legacy-host\", \"options\": { \"endpoint\": \"https://replacement.example.com\" } }","handlingStrategy":"validation","validationCode":"const ep = config.options?.endpoint?.trim();\nif (!ep) throw new Error(`legacy destination '${destination}' requires a user-supplied endpoint option`);\nnew URL(ep); // throws if malformed","typeGuard":"function hasLegacyEndpoint(config: DestinationRuntimeConfig): config is DestinationRuntimeConfig & { options: { endpoint: string } } {\n\tconst ep = config.options?.endpoint;\n\treturn typeof ep === \"string\" && ep.trim().length > 0;\n}","tryCatchPattern":"import { DestinationUnavailableError } from \"./errors\";\ntry {\n\tconst endpoint = configuredEndpoint(destination, config);\n} catch (err) {\n\tif (err instanceof DestinationUnavailableError) {\n\t\t// prompt user to supply a replacement endpoint\n\t}\n\tthrow err;\n}","preventionTips":["Always provide an endpoint option for legacy destination kinds","Validate required options at config load, before any upload is attempted","Check for empty-after-trim endpoint values (whitespace-only strings)","Document that legacy hosts have no defaults and need replacement URLs"],"tags":["configuration","endpoint","legacy","validation"],"backgroundTag":"missing-config-option","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}