[BUG] Claude in Chrome `file_upload` rejects all scheduled-task sessions with misleading error (real cause: INVALID_SESSION)

Status Closed — not planned
Reported on v2.1.149
Maintainer reply None cached
Activity 9 comments · opened May 28, 2026 · closed Jul 3, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

mcp__Claude_in_Chrome__file_upload rejects every file path when called from a Claude Code Desktop scheduled task, with a misleading error message:

Cannot upload "/path/to/file": only files the user has shared with this session can be uploaded.

The real cause (from main.log) is INVALID_SESSION, not a permission/sharing issue. Claude Desktop has two parallel session managers — CCD (used by scheduled tasks) and cowork (used by interactive chat) — and the Chrome MCP file_upload validator (Gv in app.asar) only consults the cowork manager (Ci). For CCD session IDs, Ci.getSession() returns null, so the validator throws [INVALID_SESSION] Unknown session: ... before ever reading the allowlist. The catch wrapper (Bcr / chromeFileUpload) re-emits this as the generic "shared with this session" message the user sees.

This makes file_upload structurally unusable from any CCD scheduled task. No permission/folder/sharing config the user can set will fix it — the session is rejected before the allowlist is consulted.

What Should Happen?

Either:

(a) file_upload should succeed for CCD scheduled-task sessions the same way it does for interactive cowork sessions — by having Gv consult both session managers (ln and Ci), and sourcing the allowlist for CCD sessions from the scheduled-task record's userSelectedFolders field (which is already preserved by the JSON writer but never read);

OR at minimum

(b) the error should surface the real INVALID_SESSION reason instead of masking it as "only files the user has shared". The current message sent me down hours of dead-end debugging trying to "share" the file in every conceivable way (which is impossible for CCD sessions — the validator throws before the allowlist is even checked).

Error Messages/Logs

From ~/Library/Logs/Claude/main.log (test run 2026-05-28):

2026-05-28 11:31:32 [info] Starting local session local_2baaad08-b19c-4a36-8565-fbf84e649d70 in /Users/mmhw/Downloads/MO
2026-05-28 11:31:33 [info] Mapping internal session local_2baaad08-... to CLI session 24635ca6-7c44-4fcd-afe6-2dc61c073d1f
2026-05-28 11:31:36 [info] [CCD start-timing] local_2baaad08-... preflight=801ms ... total_to_assistant=4343ms
...
2026-05-28 11:33:20 [warn] uploadLocalFile: unknown local agent mode session local_2baaad08-b19c-4a36-8565-fbf84e649d70
2026-05-28 11:33:20 [warn] [chromeFileUpload] rejected path for session local_2baaad08-b19c-4a36-8565-fbf84e649d70: /Users/mmhw/Downloads/MO/Standard Audio - Heidi.m4a ([INVALID_SESSION] Unknown session: local_2baaad08-b19c-4a36-8565-fbf84e649d70)

The [CCD start-timing] line confirms the session was spawned via the CCD path. The [chromeFileUpload] line shows the real INVALID_SESSION error that's then re-wrapped as the misleading user-facing message.

User-facing error returned to the SKILL:
> Cannot upload "/Users/mmhw/Downloads/MO/Standard Audio - Heidi.m4a": only files the user has shared with this session can be uploaded.

Steps to Reproduce

  1. Have Claude Desktop running on macOS with the Claude in Chrome extension installed + connected + authenticated.
  2. Create a CCD scheduled task — either via the /schedule skill, or by adding an entry to ~/Library/Application Support/Claude/claude-code-sessions/<workspace>/scheduled-tasks.json with cwd, cronExpression, and filePath pointing to a SKILL.md.
  3. The SKILL.md should: open any web page containing an <input type="file"> element via mcp__Claude_in_Chrome__navigate, find the input via mcp__Claude_in_Chrome__find, then call:

mcp__Claude_in_Chrome__file_upload({
paths: ["/Users/<you>/Downloads/anyfile.m4a"],
ref: "<ref from find>",
tabId: <tabId>
})

  1. Wait for the cron to fire. file_upload returns the rejection error 100% of the time, regardless of file location.

Things I tried that did NOT fix it (all empirically verified):

  • Adding userSelectedFolders: ["/Users/.../Downloads/MO"] to the task record in scheduled-tasks.json (preserved on disk; never propagated to the spawned CCD session JSON; validator throws INVALID_SESSION before reading allowlist anyway)
  • Moving the file to /tmp, ~/.claude/, or the task's cwd
  • @/folder mention in chat
  • Restarting Claude Desktop

I traced the root cause in /Applications/Claude.app/Contents/Resources/app.asar → .vite/build/index.js: Gv only calls Ci.getSession() (cowork manager); CCD session IDs return null; Bcr wraps the thrown error as the generic catch-all message. The CCD buildSessionStartEvent schema (OXt) also explicitly drops userSelectedFolders / userSelectedFiles even when set on the scheduled-task record.

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.149

Claude Code Version

Claude Code running inside Claude Desktop 1.9255.2

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Workaround in use: the affected task (tb-prep---heidi, daily 5 AM Heidi audio prep) now creates per-patient Heidi session shells with names pre-filled, then sends a desktop notification telling the user how many shells need manual audio upload + template application. The user opens each in their browser and does the file pick + template by hand.

Three suggested fixes ranked smallest → largest scope:

  1. Surface the real INVALID_SESSION error to the user instead of masking it as "only files the user has shared". Even a one-line addition like "(INVALID_SESSION: this session manager isn't recognized by file_upload; this may be a CCD scheduled task)" would have saved hours of dead-end debugging.
  1. Have Gv consult both session managers (ln and Ci). For CCD sessions, source the allowlist from the scheduled-task record's userSelectedFolders field (already preserved by the JSON writer; just never read on the spawn path).
  1. Unify the spawn paths so CCD buildSessionStartEvent carries userSelectedFolders / userSelectedFiles through the same as cowork's, registers CCD sessions in a unified registry, and lets Gv work uniformly.

Relevant files for engineering:

  • /Applications/Claude.app/Contents/Resources/app.asar → .vite/build/index.js (functions Gv, Bcr, OXt, Ci, ln, CCD vs cowork buildSessionStartEvent)
  • ~/Library/Logs/Claude/main.log (the [chromeFileUpload] rejected path warnings)
  • ~/Library/Application Support/Claude/claude-code-sessions/<workspace>/scheduled-tasks.json (CCD task records)
  • ~/Library/Application Support/Claude/claude-code-sessions/<workspace>/local_<id>.json (CCD session JSON — note userSelectedFolders key absent)
  • ~/Library/Application Support/Claude/local-agent-mode-sessions/<workspace>/local_<id>.json (cowork session JSON — userSelectedFolders: [] present as explicit empty array)

View original on GitHub ↗

8 Comments

Wangnov · 3 months ago

I can confirm this with another local repro, and it looks like the issue is not Chrome file access or the target website's file input. It is the session boundary used by the Desktop/Chrome bridge.

Environment:

  • Claude Desktop 1.9659.2 on macOS
  • Claude in Chrome connected
  • Target page: ChatGPT file input

What I observed:

  1. In a normal Claude Code session, mcp__Claude_in_Chrome__file_upload is visible/callable, but every local path I tried failed with:
Cannot upload "...": only files the user has shared with this session can be uploaded.

This included files that were otherwise readable/attached in the Claude Code session.

  1. In an interactive Cowork session, the same upload path works.

The successful session was a Cowork/local-agent-mode session titled File upload to ChatGPT. Its session id was local_<uuid>. The user-attached PDF was copied into that session's own:

~/Library/Application Support/Claude/local-agent-mode-sessions/.../local_<uuid>/uploads/<file>.pdf

Then this call succeeded:

{
  "tool": "mcp__Claude_in_Chrome__file_upload",
  "paths": [".../local_<uuid>/uploads/<file>.pdf"],
  "ref": "ref_187",
  "tabId": 1165646887
}

The returned result was:

Uploaded 1 file(s) to file input: <file>.pdf

Notably, userSelectedFolders was empty in the Cowork session metadata, so the success appears to come from the built-in allowlist for the current local session's uploads directory, not from a connected folder.

This matches the code-level diagnosis in this issue: the bridge is not simply checking whether the model can read the file. It is checking whether the current session is a recognized local/Cowork session and whether the file is under that session's allowed roots. When the session is not recognized by that manager, the user-facing error is flattened into the misleading "only files the user has shared" message.

This also clarifies the relationship to #31210 / #25871: those were about file_upload not being exposed. This is the next layer down: the tool is exposed and callable, but local path validation rejects sessions that are not registered in the same local-agent/Cowork session registry.

cc @bhosmer-ant since you confirmed the related MCP tool exposure fix in #12164. Could you route this to the Chrome/Desktop owner? The fix likely needs to either:

  • make file_upload validate against all Desktop/Claude Code session managers that can expose the tool, or
  • register these sessions in the same local-agent session registry used by Cowork, and
  • preserve the real INVALID_SESSION reason in the user-facing error instead of masking it as a file-sharing problem.
github-actions[bot] · 1 month ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

arjen-rave · 23 days ago

Hit this exact failure today (2026-08-07) on a scheduled task session. file_upload rejected a path inside the session's own outputs folder with "only files this session is allowed to read can be uploaded" — matches the INVALID_SESSION root cause described above exactly. Session was spawned via the scheduler at 07:18 UTC; the rejected upload attempt was at 10:53. Can confirm the changelog fix for "Claude in Chrome file uploads failing for files in the session's shared folders and outputs" (shipped by v1.26832.0, 2026-08-06) did not resolve this — same error, one day later, same session type. Re-opening or at minimum surfacing the real INVALID_SESSION reason (suggestion 1 in the original report) would save others the same dead-end debugging. Happy to share more session logs if useful.

cristianfraser · 19 days ago

Also happening to me on mac now, it was working fine a week ago.

kasparovabi · 18 days ago

Landed here from the same tool failing a different way. On a normal interactive session mcp__claude-in-chrome__file_upload rejects the paths argument with expected array, received undefined on every call shape, which traces back to array arguments being serialized as strings (#22394, closed as not planned). Related CDP failure is documented in #32561.

Both of those are locked now, so leaving the working alternative here for anyone who arrives from a search.

Serve the file over loopback, fetch it inside the page, attach it through DataTransfer. That gives the input real bytes from disk rather than a synthetic in-memory file.

const input = document.querySelector('input[type=file]');
const blob = await (await fetch('http://127.0.0.1:8765/cover.jpg', {mode:'cors'})).blob();
const file = new File([blob], 'cover.jpg', {type: blob.type, lastModified: Date.now()});
const dt = new DataTransfer(); dt.items.add(file);
input.files = dt.files;
input.dispatchEvent(new Event('change', {bubbles: true}));

The non-obvious part is the server side. Chrome refuses a request from an https page to 127.0.0.1 unless the response carries Access-Control-Allow-Private-Network: true. Without that header the fetch neither resolves nor rejects, it just hangs with nothing in the console.

Server and page-side helper packaged here, MIT. https://github.com/kasparovabi/claude-file-bridge

Caveats. The change event is synthetic so isTrusted is false, and sites that verify event trust will refuse it, though ordinary upload forms accept it. A single long await fetch inside the javascript tool can also exceed the 45 second CDP evaluate timeout, so park the result on window and read it on the next call.

Used it for seven images in one session after every file_upload call failed.

sjmorris-dlore · 18 days ago

Thanks for the workaround. Will try it within the next day or so.

yoelf22 · 14 days ago

Same root cause, but also reproducible from an interactive Cowork session (not just scheduled tasks) — and a newer client build now masks it as a schema error

I hit what looks like this exact bug, but from a normal interactive Cowork chat (no scheduled task involved), with a folder that was connected to the session. Sharing a clean reproduction in case it widens the scope here.

Environment

  • Claude desktop app (Cowork mode) + Claude in Chrome extension, macOS, Google Chrome.
  • Tool: mcp__claude-in-chrome__file_upload.
  • App / extension versions: _<fill in — App → About; chrome://extensions → Details>_

Two symptoms, split purely by whether the file exists

file_upload({ tabId, ref, paths: [<one path>] }), connected folder, single file:

| paths value | file exists on disk? | result |
|---|---|---|
| ["test-dummy-value"] | no | only files this session is allowed to read can be uploaded |
| ["/tmp/nope.png"] | no | same "allowed to read" message |
| ["/Users/<user>/…/img.png"] (real file) | yes | -32602 Invalid arguments … "path": ["paths"], "message": "Invalid input: expected array, received undefined" |
| byte-identical copy of that file, renamed | yes | identical paths: undefined error |
| a freshly-created 69-byte 1×1 PNG in the same folder | yes | identical paths: undefined error |

Ruled out as causes by isolating one variable at a time: filename, file size (69 bytes fails), image content (byte-identical copy fails the same), extension (.png/.jpg), the folder, and home-directory path (a short /Users/<user>/test.png for a non-existent file passes). The only determinant is whether the file exists.

Why this looks like the same bug

  • The non-existent-file path produces exactly the misleading "only files … allowed to read" message described in this issue, even though the folder was connected — i.e. the validator behaves as if nothing is shared, matching the session-manager mismatch in the root-cause analysis above.
  • The existing-file path returns paths: expected array, received undefined. On a later call the tool itself returned: "file_upload can't accept pre-read files in this session; it only takes paths … the client converted them before they reached the host and file_upload is unavailable here — tell the user instead of retrying." So a newer client build appears to pre-read the file and convert paths → pre-read files before the host sees it; the host rejects the pre-read files, which surfaces as paths being undefined. Because only existing files get read, only existing files hit this — the "exists vs not" split above.

Net: same underlying session/validator failure this issue identifies, now surfacing one layer later (as a schema error) once the client started pre-reading files. The scheduled-task framing may be narrower than the actual blast radius — this reproduced in a plain interactive Cowork session.

Regression / persistence

Worked earlier in the same conversation (several successful uploads), then broke mid-session and did not recover after: page reloads, deleting and recreating the target draft, and a full quit-and-reopen of the desktop app, the Chrome extension, and Chrome. Every other Chrome MCP tool (navigate, javascript_tool, find, screenshot) kept working — only file_upload was affected.

Suggested diagnostic

If someone can check main.log around a failing call for INVALID_SESSION, that would confirm whether interactive Cowork sessions are being routed to the unrecognized manager the same way the scheduled-task sessions are.

Workarounds confirmed (don't touch file_upload)

  • Native browser drag-and-drop / the site's own upload button.
  • Rehosting from a public URL where the target site fetches the image server-side.
  • upload_image is not a substitute — it only sources from a prior screenshot or a chat-uploaded image, not arbitrary local paths.
yoelf22 · 14 days ago

<img width="1200" height="628" alt="Image" src="https://github.com/user-attachments/assets/75a6fc30-7021-41c4-8287-9b2ab58ab8de" />

Showing cached comments. Read the full discussion on GitHub ↗