{"record":{"id":"f4f33c6910faedcb","repo":"can1357/oh-my-pi","slug":"provider-native-files-must-use-the-provider-file-c","errorCode":null,"errorMessage":"provider-native files must use the provider file channel","messagePattern":"provider-native files must use the provider file channel","errorType":"exception","errorClass":"DestinationUnavailableError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders.ts","lineNumber":158,"sourceCode":"\t\tconst command = optionString(config, \"command\");\n\t\tif (!command) {\n\t\t\tthrow new DestinationUnavailableError(destination, \"an upload command containing {file} is required\");\n\t\t}\n\t\treturn createCommandUploader(command);\n\t}\n\n\tconst uploader =\n\t\tcreateAnonymousUploader(destination, config) ??\n\t\tcreateImageHostUploader(destination, config) ??\n\t\tcreateCloudDriveUploader(destination, config) ??\n\t\tcreateObjectStorageUploader(destination, config) ??\n\t\tcreateSelfHostedUploader(destination, config) ??\n\t\tcreateLegacyUploader(destination, config) ??\n\t\tcreateDiscordUploader(destination, config);\n\tif (uploader) return uploader;\n\n\tif (destination === \"provider-files\") {\n\t\tthrow new DestinationUnavailableError(destination, \"provider-native files must use the provider file channel\");\n\t}\n\tif (metadata.family === \"local-serving\" || metadata.family === \"tunnel\") return null;\n\tthrow new DestinationUnavailableError(destination, \"no built-in uploader or serving adapter is implemented\");\n}\n\n/** Wrap an uploader with per-hash memoization so bytes upload at most once. */\nexport function memoizeUploader(\n\tuploader: BlobUploader,\n): (hash: string, request: BlobUploadRequest) => Promise<BlobPublication | null> {\n\tconst byHash = new Map<string, Promise<BlobPublication | null>>();\n\treturn (hash, request) => {\n\t\tlet pending = byHash.get(hash);\n\t\tif (!pending) {\n\t\t\tpending = uploader.upload(request).catch(error => {\n\t\t\t\tbyHash.delete(hash);\n\t\t\t\tlogger.warn(\"blob-broker: upload failed; image stays inline\", {\n\t\t\t\t\tuploader: uploader.destination,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders.ts#L140-L176","documentation":"The \"provider-files\" destination is not an upload destination — provider-native file handling goes through the provider's dedicated file channel. If a caller requests an uploader for it, createConfiguredUploader throws DestinationUnavailableError with this message to redirect the caller to the correct API. It is a routing guard, not a config failure.","triggerScenarios":"Explicitly setting \"images.urls.destination\": \"provider-files\" in config, or programmatically calling createConfiguredUploader(\"provider-files\", config). No fallback uploader exists for this id, so the branch always throws.","commonSituations":"Misunderstanding the destination list and assuming provider-files is an upload target; migrating config where a previous uploads mechanism was replaced by the provider file channel; tooling that enumerates destinations and tries to instantiate each one.","solutions":["Remove \"provider-files\" from images.urls.destination and use a supported destination (command, a self-hosted/anonymous/legacy/discord destination)","Use the provider's file API/channel directly instead of the blob broker for provider-native files","Pick a local-serving or tunnel destination if you only need a URL for shared blobs","Check BUILTIN_BLOB_DESTINATIONS metadata for destinations that return null (local-serving/tunnel) vs throwing"],"exampleFix":"// before\n\"urls\": { \"destination\": \"provider-files\" }\n// after\n\"urls\": { \"destination\": \"command\", \"command\": \"curl -sF 'file=@{file}' https://0x0.st\" }","handlingStrategy":"validation","validationCode":"if (destination === \"provider-files\") {\n\t// route through the provider file channel instead of the blob broker\n\treturn providerFileChannelUpload(request);\n}","typeGuard":"function isBrokerUploadDestination(id: string): boolean {\n\treturn id !== \"provider-files\";\n}","tryCatchPattern":"try {\n\tconst uploader = createConfiguredUploader(destination, config);\n} catch (err) {\n\tif (err instanceof DestinationUnavailableError && err.message.includes(\"provider file channel\")) {\n\t\t// switch to the provider's native file API instead of the blob broker\n\t}\n\tthrow err;\n}","preventionTips":["Treat provider-files as a non-upload id: exclude it from destination pickers","Use the provider's file API directly for provider-native file storage","Validate config values against the supported destination list at load time","When enumerating destinations, handle local-serving/tunnel (null return) separately from throwing ids"],"tags":["configuration","wrong-api","provider-files","blob-broker"],"backgroundTag":"unsupported-destination","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}