can1357/oh-my-pi · error · ToolError
tab.uploadFile() requires at least one file path
Error message
tab.uploadFile() requires at least one file path
What it means
tab.uploadFile() validates its filePaths array before doing any work; an empty array cannot identify any file, so it throws this ToolError immediately. This is a pre-condition check guarding the subsequent uploadFile call.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:2068
}
select.dispatchEvent(new EventCtor("input", { bubbles: true }));
select.dispatchEvent(new EventCtor("change", { bubbles: true }));
return selected;
}, values),
)) as string[];
} finally {
await handle.dispose().catch(() => undefined);
}
}
async #uploadFile(
selector: string,
filePaths: string[],
timeoutMs: number,
signal: AbortSignal,
session: SessionSnapshot,
): Promise<void> {
if (!filePaths.length) throw new ToolError("tab.uploadFile() requires at least one file path");
const handle = await this.#resolveActionHandle(selector, timeoutMs, signal);
try {
const absolute = filePaths.map(filePath => resolveToCwd(filePath, session.cwd));
const upload = handle as unknown as { uploadFile: (...paths: string[]) => Promise<void> };
const tagName = (await untilAborted(signal, () =>
handle.evaluate(el => (el as unknown as { tagName: string }).tagName),
)) as string;
if (tagName !== "INPUT")
throw new ToolError(
`tab.uploadFile() requires an <input type="file"> element (got <${tagName.toLowerCase()}>)`,
);
await untilAborted(signal, () => upload.uploadFile(...absolute));
} finally {
await handle.dispose().catch(() => undefined);
}
}
async #waitForUrl(pattern: string | RegExp, timeout: number, signal: AbortSignal): Promise<string> {View on GitHub (pinned to 9690622007)
Solutions
- Pass at least one existing file path in filePaths.
- Check whatever produced the list (glob, readdir) and assert it is non-empty before calling.
- If the upload is optional, skip the call entirely when the list is empty instead of invoking it.
Example fix
// before
const files = fs.readdirSync(dir).filter(f => f.endsWith(".csv"));
await tab.uploadFile("#file", files); // throws when dir has no .csv
// after
const files = fs.readdirSync(dir).filter(f => f.endsWith(".csv"));
if (files.length > 0) await tab.uploadFile("#file", files.map(f => path.join(dir, f))); Defensive patterns
Strategy: validation
Validate before calling
if (!filePaths?.length) {
throw new Error("uploadFile called with no file paths; check the file list source");
} Type guard
function hasFiles(paths: unknown): paths is [string, ...string[]] {
return Array.isArray(paths) && paths.length > 0 && paths.every(p => typeof p === "string");
} Try / catch
try {
await tab.uploadFile(selector, filePaths);
} catch (err) {
if (err.message.includes("at least one file path")) {
return skipUpload(); // upload was optional
}
throw err;
} Prevention
- Guard the paths array before calling
- Skip optional uploads when the list is empty
- Log why the file list was empty (glob miss, bad dir) to aid debugging
- Validate paths exist on disk before the call
When it happens
Trigger: Calling tab.uploadFile(selector, []) — e.g. an upload list built from zero matched files, or a caller forwarding an optional paths array that was undefined-normalized to [].
Common situations: A glob/glob-like expansion produced no matches before the call; an agent omitted the files argument; config pointed at a directory with no files so the collected list was empty.
Related errors
- Drag ${role} must be a selector string or { x: number, y: nu
- Upload filename is invalid
- Expected --${name} to be a positive integer, got ${value}
- Cache flags require --cache
- --alias requires --profile <name> or OMP_PROFILE
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9eec22591b74422e.
Report an issue: GitHub.