{"record":{"id":"7f3b3dbd6c33b3d0","repo":"can1357/oh-my-pi","slug":"destination-option-endpoint-must-be-an-absolute-ur","errorCode":null,"errorMessage":"Destination option endpoint must be an absolute URL","messagePattern":"Destination option endpoint must be an absolute URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-image-hosts.ts","lineNumber":308,"sourceCode":"\t\t\tconst getSizesParameters: Record<string, string> = { ...getSizesFields, ...getSizesOAuth };\n\t\t\tfor (const key in getSizesParameters) {\n\t\t\t\tgetSizesUrl.searchParams.append(key, getSizesParameters[key]);\n\t\t\t}\n\t\t\tconst sizesResponse = await fetchFor(config)(getSizesUrl, { method: \"GET\" });\n\t\t\tconst sizes = await jsonResponse(sizesResponse, \"flickr\");\n\t\t\treturn publication(\"flickr\", request, largestFlickrSource(sizes), { remoteId: photoId });\n\t\t},\n\t};\n}\n\nfunction requiredEndpoint(config: DestinationRuntimeConfig): string {\n\tconst endpoint = optionString(config, \"endpoint\");\n\tif (!endpoint) throw new Error(\"Missing required destination option: endpoint\");\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(endpoint);\n\t} catch {\n\t\tthrow new Error(\"Destination option endpoint must be an absolute URL\");\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new Error(\"Destination option endpoint must use HTTP or HTTPS\");\n\t}\n\treturn url.href;\n}\n\nfunction createCheveretoUploader(config: DestinationRuntimeConfig): BlobUploader {\n\tconst endpoint = requiredEndpoint(config);\n\tconst apiKey = requireCredential(config, \"apiKey\");\n\n\treturn {\n\t\tdestination: \"chevereto\",\n\t\tasync upload(request) {\n\t\t\tconst response = await fetchFor(config)(endpoint, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: multipartFile(request, \"source\", { key: apiKey, format: \"json\" }),\n\t\t\t});","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-image-hosts.ts#L290-L326","documentation":"requiredEndpoint() throws this when the configured endpoint string cannot be parsed by the URL constructor — it is present but not an absolute URL (missing scheme, malformed host, whitespace, etc.).","triggerScenarios":"Endpoint values like 'chev.example.com' (no scheme), 'localhost:80' (parsed as scheme 'localhost'), 'https://' (empty host), or a value containing spaces/newlines from a pasted config.","commonSituations":"Users pasting a hostname without 'https://', quotes or whitespace surviving YAML/JSON parsing, or a truncated URL from a bad copy-paste.","solutions":["Prefix the endpoint with https:// if you wrote a bare hostname","Trim whitespace and remove stray quotes from the endpoint value","Validate the URL in a browser or curl before saving it to config","Use the full absolute URL including scheme, e.g. https://chev.example.com/api/upload"],"exampleFix":"// before\n\"endpoint\": \"chev.example.com/api/upload\"\n// after\n\"endpoint\": \"https://chev.example.com/api/upload\"","handlingStrategy":"validation","validationCode":"const ep = config.options?.endpoint;\nif (typeof ep === \"string\" && ep.trim()) {\n\ttry { new URL(ep); } catch { throw new Error(`endpoint '${ep}' is not an absolute URL (add https://)`); }\n}","typeGuard":"function isAbsoluteHttpUrl(value: unknown): value is string {\n\tif (typeof value !== \"string\") return false;\n\ttry { const u = new URL(value); return u.protocol === \"http:\" || u.protocol === \"https:\"; } catch { return false; }\n}","tryCatchPattern":"try {\n\tconst href = requiredEndpoint(config);\n} catch (err) {\n\tif (err instanceof Error && err.message.includes(\"absolute URL\")) {\n\t\t// hint: prefix with https://\n\t}\n\tthrow err;\n}","preventionTips":["Always include the scheme (https://) when entering endpoints","Trim whitespace and strip quotes from pasted URLs","Test the endpoint with curl before saving config","Validate endpoints with new URL() at config-load time"],"tags":["configuration","url","validation"],"backgroundTag":"invalid-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}