{"record":{"id":"eb2669da32701b1e","repo":"can1357/oh-my-pi","slug":"images-urls-command-is-empty","errorCode":null,"errorMessage":"images.urls.command is empty","messagePattern":"images\\.urls\\.command is empty","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders.ts","lineNumber":82,"sourceCode":"}\n\n/** Last URL printed on stdout wins; uploader tools often log progress first. */\nexport function extractUploadUrl(stdout: string): string | null {\n\tlet last: string | null = null;\n\tfor (const match of stdout.matchAll(URL_PATTERN)) {\n\t\tlast = match[0].replace(/[)\\],.'\"]+$/, \"\");\n\t}\n\treturn last;\n}\n\n/**\n * Build an uploader from an argv template. Placeholders, substituted after\n * splitting (paths with spaces stay one argument): `{file}` temp file path,\n * `{mime}` MIME type, `{ext}` bare extension.\n */\nexport function createCommandUploader(template: string): BlobUploader {\n\tconst argvTemplate = splitCommandTemplate(template);\n\tif (argvTemplate.length === 0) throw new Error(\"images.urls.command is empty\");\n\tif (!argvTemplate.some(arg => arg.includes(\"{file}\"))) {\n\t\tthrow new Error(\"images.urls.command must reference {file}\");\n\t}\n\treturn {\n\t\tdestination: \"command\",\n\t\tasync upload(request: BlobUploadRequest): Promise<BlobPublication> {\n\t\t\tconst { bytes, mimeType, extension } = request;\n\t\t\tconst file = path.join(os.tmpdir(), `omp-blob-upload-${crypto.randomUUID()}.${extension}`);\n\t\t\tawait Bun.write(file, bytes);\n\t\t\ttry {\n\t\t\t\tconst argv = argvTemplate.map(arg =>\n\t\t\t\t\targ.replaceAll(\"{file}\", file).replaceAll(\"{mime}\", mimeType).replaceAll(\"{ext}\", extension),\n\t\t\t\t);\n\t\t\t\tconst cwd = getProjectDir();\n\t\t\t\tif (!directoryIsEnterableSync(cwd)) {\n\t\t\t\t\tthrow new Error(`Project directory is not accessible: ${cwd}`);\n\t\t\t\t}\n\t\t\t\tconst proc = Bun.spawn(argv, { stdin: \"ignore\", stdout: \"pipe\", stderr: \"pipe\", cwd });","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders.ts#L64-L100","documentation":"createCommandUploader builds a BlobUploader from an argv template string (images.urls.command in config). After splitting the template, if no arguments remain the command is useless, so it throws immediately. This is a synchronous, configuration-time validation error.","triggerScenarios":"Configuring images.urls.command as an empty string or whitespace-only string; the template consists solely of characters that splitCommandTemplate strips (quotes/spaces), yielding zero argv entries.","commonSituations":"Config placeholder left unfilled (\"command\": \"\") after copying a config template; YAML/TOML key present but value empty; quoting mistakes that collapse the value to nothing.","solutions":["Set images.urls.command to a real command template, e.g. \"curl -F 'file=@{file}' https://0x0.st\"","Remove the empty images.urls.command key if you do not want the command destination","Check the config file section actually saved (empty string vs commented-out line)","Validate the template contains {file} as well, since a non-empty template without {file} throws the next error"],"exampleFix":"// before\n\"urls\": { \"command\": \"\" }\n// after\n\"urls\": { \"command\": \"curl -sF 'file=@{file}' https://0x0.st\" }","handlingStrategy":"validation","validationCode":"const cmd = config.images?.urls?.command;\nif (typeof cmd !== \"string\" || cmd.trim().length === 0) {\n\tthrow new Error(\"images.urls.command must be a non-empty command template\");\n}","typeGuard":"function hasCommandTemplate(c: unknown): c is { command: string } {\n\treturn typeof c === \"object\" && c !== null && typeof (c as { command?: unknown }).command === \"string\"\n\t\t&& (c as { command: string }).command.trim().length > 0;\n}","tryCatchPattern":"try {\n\tconst uploader = createConfiguredUploader(\"command\", config);\n} catch (err) {\n\tif (err instanceof Error && err.message === \"images.urls.command is empty\") {\n\t\t// prompt user to fill images.urls.command in config\n\t}\n\tthrow err;\n}","preventionTips":["Never leave images.urls.command as an empty string; comment out the whole block instead","Validate config at load time with a schema","Include {file} whenever you set a command template","Keep a working example command in config comments"],"tags":["configuration","validation","command-uploader"],"backgroundTag":"empty-config-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}