Skip to content

attachments: dragging file or pasting file path into input fails session setup (Prompt.Base64 500 on /prompt) #45558

Closed

Description

@shangweijun

Summary

After updating to v0.0.0-beta-18387, dragging a file into the TUI input box, or copying a file path and pasting it into the input, causes the session to fail to start. The server returns HTTP 500 on POST /api/session/<id>/prompt because the file is treated as an image attachment and its base64 payload fails the Prompt.Base64 schema validation. Result is surfaced to the user as "Failed to set up session".

Environment

  • opencode version: v0.0.0-beta-18387
  • OS: Windows 10.0.26200.9168 (win32 x64)
  • Terminal: TERM=xterm-256color (launched via Git bash)
  • Shell: COMSPEC=C:\WINDOWS\system32\cmd.exe (opencode config shell: "bash")
  • Install/channel: beta
  • Active plugins: none found in config

Reproduction

  1. Launch the TUI with opencode2 (no config/images involved — global opencode.json has no embedded base64 images, project has no opencode.json).
  2. In the input box, either:
    • drag a file (e.g. a source/text file) into the input, or
    • copy a file path and paste it into the input.
  3. Send the message (this is typically the first prompt of the session).
  4. Observe "Failed to set up session" in the TUI.

Pure-text first messages work fine; only messages that contain a dragged file or a pasted file path trigger the failure.

Expected Behavior

Dragging a file or pasting a file path into the input should attach/reference the file as a file (text/context), not route it through the image-attachment base64 path. The session should set up and the message should be delivered.

Actual Behavior

The server logs a 500 with a schema validation error originating from the image-attachment normalization path:

POST /api/session/<id>/prompt  http.status=500
cause: Error: Schema validation failed
  at Session.normalizeImageAttachment (...)
  at Session.materializeAttachment (...)
  at Session.resolvePrompt (...)
  at Session.prompt (...)
  [cause]: expected "a string matching the RegExp ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$"
           identifier: "Prompt.Base64"

The attached file content (or the pasted path data, which can include a data:image/...;base64, prefix or other non-base64 characters) does not satisfy the strict Prompt.Base64 pattern, so the prompt is rejected before the model is ever called.

Additional Context

  • The failure is consistent (100% reproducible) on this version whenever the first message carries a dragged file or pasted file path.
  • The same action worked before the update; this appears to be a regression in v0.0.0-beta-18387.
  • Workaround: avoid drag-drop / paste-path; reference files with the @ file-reference (mention) syntax instead, which goes through the text/context channel and does not trigger image base64 validation.
  • The background service itself is healthy (opencode2 service status returns a valid address); the failure is isolated to the prompt-with-attachment handling.

Redacted server log excerpt (username/workspace paths removed):

timestamp=... level=INFO run=de674f6b cause="Error: Schema validation failed
    at Session.normalizeImageAttachment (...)
    at Session.materializeAttachment (...)
    at Session.resolvePrompt (...)
    at Session.prompt (...)
  [cause]: Error: {... "expected":"a string matching the RegExp ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", "identifier":"Prompt.Base64" ...}
" http.span=387 role=server http.method=POST http.url=/api/session/<id>/prompt http.status=500

Activity

  1. github-actions commented on Aug 27, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. MilkyWay008 commented on Aug 27, 2026

    @MilkyWay008

    The 500 on /api/session//prompt after beta-18387 is the attachment normalizer routing non-image files through the Prompt.Base64 validator, the schema error is just it tripping on a path that was never meant to hit that regex. On Windows 10.0.26200 with git bash the pasted Windows path is the trigger, so the @-mention workaround sidesteps the whole branch, and #44310/#35697 cover the same normalizer area so the fix will probably land together. I keep a portable Hermes OTG on a USB stick as my rescue agent for exactly these update regressions, https://github.com/MilkyWay008/Hermes-OTG if you want to poke at it.

  3. guillemus commented on Sep 13, 2026

    @guillemus

    Still reproducing on OpenCode 2.0.3, latest channel, on macOS (Apple Silicon).

    I attempted to attach an approximately 11 MB .har file. Submitting the prompt fails with the same Prompt.Base64 validation error reported here.

    Relevant server log, September 13, 2026 (UTC), with the session ID redacted:

    timestamp=2026-09-13T14:24:16.669Z
    level=INFO
    run=9fce6d3c
    role=server
    http.method=POST
    http.url=/api/session/<session-id>/prompt
    http.status=500
    
    Error: Schema validation failed
        at SessionPrompt.normalizeImageAttachment (/$bunfs/root/chunk-p4tygj4a.js:119:23)
        at SessionPrompt.materializeAttachment (/$bunfs/root/chunk-s27nhvzh.js:24:19733)
        at SessionPrompt.prepare (/$bunfs/root/chunk-p4tygj4a.js:134:4841)
        at Session.prompt (/$bunfs/root/chunk-k0edh0b3.js:431:6975)
    
    Validation identifier: Prompt.Base64
    Issue: InvalidValue
    Expected pattern:
    ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
    

    The same stack trace and HTTP 500 appear repeatedly across multiple session IDs between 14:22 and 14:24 UTC.

  4. darellchua2 commented on Sep 25, 2026

    @darellchua2

    Root-caused this one (and #50336 — same bug) on v2.0.16.

    Prompt.Base64 in packages/schema/src/prompt.ts validates with

    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/

    Regex engines recurse per iteration of the (?:…{4})* group, so past a few MB the check stops working. Under Bun 1.4.2/JSC (the runtime embedded in the shipped binary) it silently returns false for canonical base64 — that's the InvalidValue schema issue in the 500 logs from POST /api/session/<id>/prompt. Under V8 the same input throws RangeError instead. materializeAttachment base64-encodes attachments up to the 20 MB limit and runs this check on the result, so any @-attached file over roughly 5 MB (any type — PDF, xlsx, …) fails the prompt with a 500 before the provider is called. Small attachments pass, which is why images seem fine.

    Minimal repro:

    const re = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
    const s = require("crypto").randomBytes(20 * 1024 * 1024).toString("base64")
    re.test(s) // Bun/JSC: false (canonical base64!) · Node/V8: RangeError

    The grouped-quantifier shape can't be fixed with a better regex. Canonical base64 is decidable with a length preflight plus a structurally linear pattern (a single char-class star compiles iteratively, no recursion):

    const n = s.length
    if (n % 4 !== 0) return false
    if (!/^[A-Za-z0-9+/]*={0,2}$/.test(s)) return false
    const pads = s.endsWith("==") ? 2 : s.endsWith("=") ? 1 : 0
    return (n - pads) % 4 !== 1

    I fuzzed 20k mutated canonical strings against both — zero mismatches (AB==/ABC= accept, AB=/ABCD==/A=== reject, "" accepted, base64url -_ rejected, same as the current pattern). The 27.9M-char encoding of a 20 MB file passes in 6 ms; the current pattern returns false on it in 156 ms under Bun 1.4.2.

    Happy to send a PR if useful.

  5. AndreyMentor commented on Oct 5, 2026

    @AndreyMentor

    Adding data from a second report on the current desktop release. This is the same failure mode (Prompt.Base64 validation -> HTTP 500 on POST /api/session/<id>/prompt), but with a different surface and trigger, so it may help narrow the root cause.

    Still present in 2.0.11 (desktop)

    • opencode version: 2.0.11 (OpenCode desktop app, Electron)
    • OS: Windows 10 Pro 10.0.19045 (win32 x64)
    • Config: ~/.config/opencode/opencode.jsonc with "model": "opencode-go/qwen3.7-plus"
    • Plugins: none

    This is roughly six weeks after beta-18387, on a different surface (desktop app rather than TUI).

    Trigger is not drag-and-drop

    The report here involves pasting/attaching a screenshot in the desktop composer, not dragging a file or pasting a file path. Pure-text prompts in the same session succeed normally.

    Frequency

    34 failures inside a single burst, 2026-10-04T13:00:45.759Z to 2026-10-04T13:09:47.871Z (~9 minutes). The cadence looks like retries: most entries repeat 0.4-0.5s apart, with 1-200s gaps between groups.

    CLI path works - this is the most useful clue

    Attaching the same kind of file through the CLI succeeds, on both installed versions:

    opencode run --model opencode-go/mimo-v2.6-flash --file test.png --auto "describe this image"
    
    • 2.0.11 -> 10s, correct image description
    • 2.0.22 -> 5s, correct image description

    So --file produces a Prompt.Base64-valid payload while the desktop composer does not. That points at the desktop client's encoding/normalization step rather than the server-side schema itself. Possible differences worth checking on the desktop path: a data:image/...;base64, prefix being left in place, whitespace or newlines surviving from clipboard encoding, or a base64url alphabet without padding.

    User-visible symptom

    The prompt is dropped silently, the session stays in running, and the composer shows only "Queue" for every subsequent message. From the outside the agent looks like it hung or stopped mid-task, with nothing in the UI to explain why.

    Diagnostics note

    This failure is logged as level=INFO with cause= rather than level=ERROR, so it does not show up in a normal error search of the log. Searching for Schema validation failed was the only way to find it.

    Possibly related

    #35112 reports image inputs on qwen3.7-plus via OpenCode Go being rejected upstream by a 6 MB request body limit. That is a different failure (provider-side BadRequest.TooLarge, not a server-side 500), but large screenshots on the Go provider may hit both.

  6. rojosinalma commented on Oct 6, 2026

    @rojosinalma

    Still reproduces on 2.0.22 (Linux x86-64, opencode serve, prompt sent by a web client as a data:application/pdf;base64,… URI). A 7 MB PDF fails POST /api/session/<id>/prompt with HTTP 500 and the same Schema validation failed … Prompt.Base64 … InvalidValue cause from SessionPrompt.normalizeImageAttachment; a small PDF, a small PNG and a 1.8 MB PNG (which does get resized to JPEG) all go through.

    Isolated the threshold in the runtime the binary embeds (Bun 1.4.2 / JSC): the Prompt.Base64 regex returns false for canonical, padded base64 as soon as the string is longer than ~8.25 M chars, i.e. an attachment of roughly 5.9 MB or more. Below that it is true, above it false, independent of content (repeating bytes and random bytes behave the same), and it never throws:

    const re = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
    for (const bytes of [6189056, 6193152, 7 * 1024 * 1024]) {
      console.log(bytes, re.test(Buffer.alloc(bytes, 7).toString("base64")));
    }
    // 6189056 true
    // 6193152 false
    // 7340032 false

    So every attachment between ~5.9 MB and the 20 MB limit that materializeAttachment enforces is rejected as a 500, regardless of transport (@ mention, drag and drop, or a data: URI from an API client). #51272 replaces the regex with a decode-based check and would fix this; the needs:issue label on it looks like the bot artefact mentioned there, since this issue and #50336 are linked in its body.

  7. Skeptomenos commented on Oct 15, 2026

    @Skeptomenos

    Still reproducible on 2.0.29 (042d083), Linux arm64, no plugins, directly against opencode serve with a local synthetic model.

    In a fresh session, POST this input to /api/session/<id>/prompt:

    const bytes = Buffer.alloc(6_000_000, 65);
    const input = {
      text: "test",
      resume: false,
      files: [{ name: "large.txt", uri: `data:text/plain;base64,${bytes.toString("base64")}` }],
    };

    It returns HTTP 500 with zero response bytes. With 4,000,000 bytes it returns 200. Two 4,000,000-byte files in one prompt also return 200 and reach the local model intact, despite the larger request body. Plain data: text and PDF-signature bytes reproduce it too.

    On this binary, 5,345,276 decoded bytes pass and 5,345,277 fail. This is an observed runtime boundary, not a portable size limit. Exactly 20 MiB returns the empty 500; 20 MiB plus one byte returns the intended JSON 400.

    The on-disk server log identifies SessionPrompt.normalizeImageAttachment and Prompt.Base64. The same valid base64 makes the pinned validator return false in Bun/JSC. This supports the existing diagnosis and #51272. Expected: attachments within the declared 20 MiB limit pass admission.

  8. geverl commented on Oct 22, 2026

    @geverl

    Following the suggestion from the bot on #58042 (whose report I filed as a duplicate of this one) — two UX points, plus some measurements that may be useful as regression-test material for #51272.

    UX points

    1. Enforce the limit at attach/upload time. The code intends to allow 20 MB, but the regex fails at ~5.3 MB of raw file data, so users hit a wall well before the documented limit. Failing at attach time with a clear message ("this file is too large to attach") is much easier to act on than a session that silently becomes unreadable days later.

    2. Report decode failures per attachment / per message. Today a single oversized attachment makes GET /api/session/{id}/message return 500 for the whole session, and the Desktop app shows only UnknownError: Unexpected server error. Check server logs for details. Every message in that session becomes unreachable, including the ones that are perfectly valid. Reporting which attachment failed would at least make the problem diagnosable from the UI.

    Measurements for the fix

    Bisected against a running 2.1.2 server. One attachment per message, valid base64, everything else in the message valid:

    base64 data length result
    7,066,668 OK
    7,077,876 OK
    7,077,880 failed to decode
    7,200,000 failed to decode

    The limit is per attachment, not per message and not per response:

    attachments total base64 response size result
    1 × 4,000,000 5,333,336 5.5 MB OK
    2 × 3,000,000 8,000,000 8.2 MB OK
    2 × 4,000,000 10,666,672 10.9 MB OK
    1 × 5,400,000 7,200,000 — failed to decode

    Two things I'd suggest checking when the replacement lands:

    • The failing boundary is ~5.3 MB raw (7.08M base64 characters), so the current behaviour rejects attachments roughly 4× earlier than the 20 MB the code intends. It's worth confirming the 20 MB path is actually reachable rather than hitting some other bound first.
    • The regression test should cover a string long enough to trip the old behaviour (≥ 8 MB base64 is a safe choice) and a multi-attachment message, since the failure is per attachment rather than per message.

    For context on impact: in one of my sessions this made three ~7.3 MB PDFs unreadable, and the failure took the entire session history with it — the three sessions I had to recover were recovered by rebuilding the v2 rows from the legacy v1 message/part tables, which are still present in opencode.db after the migration.

  9. neriousy commented on Oct 23, 2026

    @neriousy
    Member

    Preserving measurements from #58116: a valid file attachment around 3.2 MB triggers the Base64 validation RangeError/HTTP 500 path. The reporter also suggests rejecting unsupported binaries before Base64 materialization to avoid the expensive validation path.

  10. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the report, and to everyone who added logs here.

    I reproduced this on 2.1.4. In the TUI I attached a ~6.8 MB text file to the first prompt (as an @ mention, which reaches the same server-side attachment code as a dragged or pasted path) and sent it. It failed with "Failed to set up session — UnexpectedStatus: 500", the same Prompt.Base64 failure. The cause is the old base64 check: on Bun its regex starts rejecting valid base64 once an attachment reaches about 6 MB. A 4.4 MB file sent the same way went through.

    On the latest dev (a882cf9) the same steps work: the file is attached and the message is delivered. The fix is #58811, which replaced the regex with a check that runs in linear time. It should also be in v2.2.0, though I only tested dev. So this is most likely fixed and should reach you with the next update.

    I could only reproduce this with files over about 6 MB. If you still see it after updating, especially with a small file or a pasted Windows path from Git Bash, please reply with your version, the file size and the server log line.

    The bot will close this issue in 1 day unless you reply that it still happens.

  11. geverl commented on Oct 28, 2026

    @geverl

    Confirmed fixed on 2.2.0 — including one case the reproduction above doesn't cover.

    Everything reported so far is the write path (POST /api/session/:id/prompt). The same Prompt.Base64 also gates decoding of already-stored messages, which is how it presents as a 500 on GET /api/session/:id/message: a single oversized attachment makes the entire session unreadable, including every valid message in it.

    Test

    A v1-era database with one session containing three application/pdf attachments stored inline at 9,756,528 / 9,788,748 / 9,824,192 base64 characters (~7.3 MB each). They were written by 1.18.32, before any of this validation existed, so the bytes were never re-encoded or re-uploaded. The same database file, unmodified, served by two runtimes:

    runtime GET /api/session/:id/message
    2.0.26 500, 112-byte error body
    2.2.0 200, 31,034,785 bytes, ~0.5 s

    The 2.2.0 response returns all 23 messages of that session with all three PDFs intact.

    What this means

    • fix(schema): validate base64 attachments in linear time #58811 covers the read path as well as the write path, so the fix is complete in both directions.
    • Stored data was never corrupt — only the validator was wrong. Anyone with a session that currently 500s should get it back by upgrading alone; no repair, migration or re-upload is needed.
    • Worth folding into the release note: the symptom users actually hit is not a failed prompt but a session that silently stops opening days later.

    For the record, #58042 (closed as a duplicate of this) was specifically this read-path variant, and #51272 was the earlier attempt at the same fix; #58811 supersedes it.

  12. opencodejr commented on Oct 29, 2026

    @opencodejr

    Closing this as completed, since nobody has replied in the past day to say it still happens.

    To recap: the Prompt.Base64 HTTP 500 ("Failed to set up session") reproduced on 2.1.4 with a ~6.8 MB attachment. On the latest dev (a882cf9) the same steps work, thanks to the linear-time base64 check from #58811. The fix should reach you in the next release you update to.

    If you still see this on a newer version, please comment here or open a new issue. It would help to include your version, the attachment's size and type, how you attached it, and the server log line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions