{"record":{"id":"a99e706de05e8501","repo":"jackwener/OpenCLI","slug":"failed-to-attach-file","errorCode":null,"errorMessage":"Failed to attach file","messagePattern":"Failed to attach file","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/claude/ask.js","lineNumber":112,"sourceCode":"                throw new CommandExecutionError(`Could not switch to ${wantModel} model`);\n            }\n            // Post-toggle settle dropped — the next CDP eval (setAdaptiveThinking) gives\n            // React enough time to flush aria-checked updates between rountrips.\n        }\n\n        const thinkResult = await withRetry(() => setAdaptiveThinking(page, wantThink));\n        if (!thinkResult?.ok && wantThink) {\n            throw new CommandExecutionError('Could not enable Adaptive thinking');\n        }\n        // Post-toggle settle dropped — the next CDP eval (sendMessage / sendWithFile)\n        // gives React enough time to flush aria-checked updates.\n\n        if (kwargs.file) {\n            const baseline = await withRetry(() => getBubbleCount(page));\n            try {\n                const fileResult = await sendWithFile(page, kwargs.file, prompt);\n                if (fileResult && !fileResult.ok) {\n                    throw new CommandExecutionError(fileResult.reason || 'Failed to attach file');\n                }\n            } catch (err) {\n                // SPA navigates after send; \"Promise was collected\" means send succeeded\n                if (!String(err?.message || err).includes('Promise was collected')) throw err;\n            }\n            // Pre-waitForResponse settle dropped — waitForResponse's first 3 s polling\n            // tick covers the same window without an unconditional sleep.\n            const result = await waitForResponse(page, baseline, prompt, timeoutMs);\n            if (!result) {\n                throw new EmptyResultError(\n                    'claude ask',\n                    `No Claude response appeared within ${timeoutSeconds}s. Re-run with a higher --timeout if the model is still generating.`,\n                );\n            }\n            return [{ response: result }];\n        }\n\n        const baseline = await withRetry(() => getBubbleCount(page));","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/claude/ask.js#L94-L130","documentation":"sendWithFile() returned a failure result while attaching kwargs.file alongside the prompt; the library raises CommandExecutionError with the specific reason (or the generic 'Failed to attach file'). A thrown error is swallowed only if it matches 'Promise was collected', which actually indicates success.","triggerScenarios":"`claude ask ... --file photo.png` where the file input/upload fails: nonexistent file path, unsupported type, upload rejected by the page, or fileResult.ok false for any reason.","commonSituations":"Typo in the --file path; attaching a file type Claude rejects; file too large; SPA upload widget not ready when the automation interacts with it.","solutions":["Verify the --file path exists and the file type is supported (image, PDF, text).","Retry the command — transient upload widget timing may resolve on a second run.","Send the prompt without --file, or attach the file manually in the browser if automation keeps failing."],"exampleFix":"// before\nopencli claude ask \"describe\" --file ./reprot.pdf   # typo, file missing\n// after\nopencli claude ask \"describe\" --file ./report.pdf","handlingStrategy":"validation","validationCode":"import { statSync } from 'fs';\nconst ALLOWED = /\\.(png|jpe?g|gif|webp|pdf|txt|md)$/i;\nfunction validateFile(p) {\n  const s = statSync(p); // throws if missing\n  if (s.size > 30 * 1024 * 1024) throw new Error('file too large for Claude upload');\n  if (!ALLOWED.test(p)) throw new Error('unsupported file type');\n}\nvalidateFile(opts.file);","typeGuard":null,"tryCatchPattern":"try {\n  return await opencli.claude.ask(prompt, { file: p, timeout: 300 });\n} catch (e) {\n  if (/Failed to attach file|attach/i.test(e.message)) {\n    return await opencli.claude.ask(prompt); // degrade to text-only\n  }\n  throw e;\n}","preventionTips":["Stat the --file path and check extension/size before invoking.","Use supported types (images, PDF, text) within Claude's size limits.","Retry once before giving up; upload widgets can be flaky."],"tags":["file-upload","browser-automation","io"],"backgroundTag":"file-attach-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}