{"record":{"id":"9ae39b202495afc6","repo":"can1357/oh-my-pi","slug":"an-upload-command-containing-file-is-required","errorCode":null,"errorMessage":"an upload command containing {file} is required","messagePattern":"an upload command containing (.+?) is required","errorType":"validation","errorClass":"DestinationUnavailableError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders.ts","lineNumber":142,"sourceCode":" * Serving destinations deliberately return `null`; the broker selects those\n * through its separate serve-kind predicate. Registry entries known to be\n * unusable, and active entries without an implementation, fail explicitly\n * before an upload can issue a network request.\n */\nexport function createConfiguredUploader(\n\tdestination: BlobDestinationId,\n\tconfig: DestinationRuntimeConfig,\n): BlobUploader | null {\n\tconst metadata: BlobDestinationMetadata = BUILTIN_BLOB_DESTINATIONS[destination];\n\tif (metadata.status === \"incompatible\" || metadata.status === \"defunct\") {\n\t\tconst reason = metadata.reason ?? `the destination is classified as ${metadata.status}`;\n\t\tthrow new DestinationUnavailableError(destination, reason);\n\t}\n\n\tif (destination === \"command\") {\n\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;","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders.ts#L124-L160","documentation":"For the \"command\" destination, createConfiguredUploader reads the \"command\" option from the destination runtime config and requires it to be a non-empty string, because createCommandUploader needs a {file}-referencing template. If absent, it throws DestinationUnavailableError with this message instead of proceeding.","triggerScenarios":"Setting \"images.urls.destination\": \"command\" without an accompanying \"images.urls.command\" value; the command value is empty string, whitespace, or the config key is misspelled (e.g. \"cmd\") so optionString returns undefined.","commonSituations":"Incomplete config migration where destination was set but the command field was not; typo in the key name; the command string defined in the wrong config section.","solutions":["Add a command template referencing {file}, e.g. \"command\": \"curl -sF 'file=@{file}' https://0x0.st\"","Fix the key spelling so it is exactly \"command\" in the command destination's config object","Or change images.urls.destination to a different supported destination","Validate the config shape: destination and command live under images.urls"],"exampleFix":"// before\n\"urls\": { \"destination\": \"command\" }\n// after\n\"urls\": { \"destination\": \"command\", \"command\": \"curl -sF 'file=@{file}' https://0x0.st\" }","handlingStrategy":"validation","validationCode":"if (config.images?.urls?.destination === \"command\" && typeof config.images?.urls?.command !== \"string\") {\n\tthrow new Error(\"destination 'command' requires images.urls.command to be set\");\n}","typeGuard":"function commandConfigComplete(c: unknown): c is { destination: \"command\"; command: string } {\n\treturn typeof c === \"object\" && c !== null\n\t\t&& (c as { destination?: unknown }).destination === \"command\"\n\t\t&& typeof (c as { command?: unknown }).command === \"string\"\n\t\t&& (c as { command: string }).command.length > 0;\n}","tryCatchPattern":"try {\n\tconst uploader = createConfiguredUploader(\"command\", config);\n} catch (err) {\n\tif (err instanceof DestinationUnavailableError && err.message.includes(\"an upload command containing {file}\")) {\n\t\t// add images.urls.command with a {file} template\n\t}\n\tthrow err;\n}","preventionTips":["Always pair destination \"command\" with a non-empty command value","Validate the config with a schema at load time","Watch for key typos: the option is exactly \"command\"","Keep destination and command in the same config object (images.urls)"],"tags":["configuration","missing-config","command-uploader","blob-broker"],"backgroundTag":"missing-config-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}