Skip to content

session: inline attachments over ~5.3 MB cannot be decoded (HTTP 500 on session messages) #58042

Closed as duplicate

Description

@geverl

Description

Inline file attachments whose base64 payload is larger than 7,077,879 characters (about 5.3 MB of raw file data) cannot be decoded by the session message API. The request fails with failed to decode session message and HTTP 500, which makes the whole session unopenable even though only one attachment is too large.

The limit lives in the v2 Prompt.Base64 schema, and data is a required field of Prompt.FileAttachment, so there is no supported way to reference a large file instead of inlining it. Sessions that attached a PDF larger than ~5.3 MB therefore cannot be represented at all.

The threshold was measured by bisection against the running server:

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

Server log:

level=ERROR message="failed to decode session message" sessionID=ses_… messageID=msg_…
level=INFO  message="Sent HTTP response" http.method=GET http.url=/api/session/ses_…/message http.status=500

OpenCode version

2.1.2 (the CLI bundled with the Desktop app, ai.opencode.desktop/cli/2.1.2-x64). The standalone CLI on PATH was 2.0.26. Install/channel: latest.

Operating System

Windows 11 Education, 10.0.26300 (win32 x64)

Terminal

TERM=xterm-256color, no TERM_PROGRAM/COLORTERM (not Windows Terminal). Shell: C:\WINDOWS\system32\cmd.exe.

Plugins

None — ~/.config/opencode/opencode.jsonc contains only $schema.

Steps to reproduce

  1. Create a user message with an inline attachment of roughly 5.4 MB (e.g. a PDF), so that its base64 data string is about 7.2 MB long.
  2. Read the session messages: GET /api/session/{sessionID}/message.
  3. Observe HTTP 500 and the log line above.

Expected behavior

Either of these would be reasonable:

  • Inline attachments should be allowed up to a size that covers real-world documents (a multi-hundred-page PDF is common), with the limit documented and enforced at upload time rather than at read time.
  • Or large attachments should be stored out-of-band (a blob/file table plus a reference), so that Prompt.FileAttachment does not need to inline the whole file.

At minimum, the failure should be reported per attachment rather than making the entire session unreadable.

Actual behavior

A single oversized attachment makes GET /api/session/{id}/message return HTTP 500 for the whole session, so the session disappears from the Desktop app with UnknownError: Unexpected server error. Check server logs for details.

Additional context

  • This was found while recovering three sessions that a v1→v2 upgrade had left unreadable; see the companion issue about the migration writing stub rows for opencode-go sessions.
  • The limit bites exactly on the kind of file a user is likely to attach. In the affected session the three PDFs were 7.3 MB, 7.3 MB and 7.4 MB raw (9.8 MB base64 each), while the attachments small enough to pass totalled 1.3 MB.
  • Prompt.FileAttachment requires data, mime and source. source does support {type:"uri", uri} as well as {type:"inline"}, but a uri source still requires data to be present, so it cannot be used to avoid the limit.
  • Local workaround used: drop the oversized attachment from the message. The original bytes remain available in the legacy part table of opencode.db.

Activity

  1. geverl commented on Oct 22, 2026

    @geverl
    Author

    Companion issue: #58041 - v1 sessions with the opencode-go provider fail to decode after upgrade (HTTP 500).

    That is the separate defect that made the three sessions unreadable in the first place; this issue covers the attachment size limit that independently prevents large PDFs from being represented at all.

  2. opencode-agent commented on Oct 22, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report and the bisection tables. I think this is a duplicate of #45558.

    The ~7.08M-character threshold isn't an intended size limit. Prompt.Base64 in packages/schema/src/prompt.ts checks base64 with the regex ^(?:[A-Za-z0-9+/]{4})*(?:…)?$. In the Bun runtime that ships with OpenCode, that regex silently returns false for valid base64 once the string is a few MB long. That's why the limit is per attachment and not per message or response. The same schema runs when a stored message is read back, so one large attachment makes the whole session fail to decode. The limit the code actually sets for an attachment is 20 MB. A fix that replaces the regex is open in #51272. Since your stored data is valid base64, sessions like yours should load again once that fix ships.

    Possible duplicates and related issues, ordered by likelihood:

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different. Your suggestions to enforce limits when a file is uploaded and to report decode failures per attachment are worth raising on #45558 or #51272.

  3. geverl commented on Oct 22, 2026

    @geverl
    Author

    Thanks for the diagnosis — that matches what I measured, and I'm happy for this one to be closed as a duplicate of #45558. The Bun regex behaviour explains my results exactly, and I'd had attributed the threshold to an intentional size limit.

    Three things worth adding, and one correction.

    Precise threshold and controls, usable as a regression test

    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 — these all decode fine:

    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

    One thing that seems worth flagging: the code intends to allow 20 MB, but the failure actually surfaces at ~5.3 MB of raw file data (7.08M base64 characters), so in practice attachments are rejected roughly 4× earlier than the intended limit suggests. It would be worth making sure the replacement in #51272 is exercised at ≥ 8 MB, and that the 20 MB path is actually reachable rather than failing at some other bound.

    A caveat about already-broken data

    Because the fix is in the schema, it should make other users' stored sessions load again with no action on their part — which is the good outcome. For my own data it won't be automatic: I had already worked around this locally by dropping the three oversized attachments from the affected user messages, so the fix won't restore them for me. The original bytes are still in the legacy part table of opencode.db, so I can restore them myself if that becomes desirable. If a data migration is planned as part of the fix, it would have been better not to have dropped them — but no action is needed from anyone for my case.

    Correction: #58041 is not the same bug

    Your duplicate list also includes #58041. That's my other report, and I don't think it shares the root cause — it involves no base64 at all:

    • The session New session - 2026-10-18T02:09:41.250Z contains one part in total, no file attachments, and its largest stored blob is 414 bytes. It still returned HTTP 500.
    • The three messages originally reported as failing had v1 blobs of 464, 448 and 1262 bytes.
    • The correlation is total. Of the 180 messages that exist in both the v1 message table and the v2 session_message table, all 49 produced by provider opencode-go were migrated as stubs, and none of the 131 produced by provider opencode were:
    provider model migrated as stub count
    opencode muse-spark-1.3-contributor-free no 129
    opencode deepseek-v4-flash-free no 2
    opencode-go muse-spark-1.3-contributor yes 26
    opencode-go gemini-4-argon-contributor yes 23

    The stub contains "model":{} and no "time", so the migration is losing the model reference and the timestamp — it isn't failing on a long string. reasoningEncryptedContent in those sessions maxes out at 21,906 characters, far below any threshold where this regex misbehaves.

    I've put the same evidence on #58041. If it turns out the model lookup in the v1→v2 migration does route through the same base64 check, I'd be glad to be wrong — but on the evidence above the two look independent, and #58041 needs a different fix from #51272.

    UX points

    I raised these two on #58042 before your reply; moving them to #45558 as you suggested:

    • Enforce the limit when a file is attached, with a clear message, rather than letting a session become unreadable later.
    • Report decode failures per attachment and per message instead of failing the whole GET /api/session/{id}/message with a 500. The current blast radius is the entire session, which is how three of my sessions disappeared without any in-app indication of what was wrong.
  4. opencode-agent commented on Oct 23, 2026

    @opencode-agent
    Contributor

    Closing this as a duplicate of #45558, since there was no reply saying how it's different. The ~5.3 MB per-attachment limit and the HTTP 500 on GET /api/session/{id}/message both come from the Prompt.Base64 regex failing on multi-MB base64. #55110 reported the same symptom when a session is loaded and was closed the same way. Closing just keeps the discussion in one place; the bug is still open. The proposed fix, PR #51272, hasn't been merged yet, so please subscribe to #45558 or #51272 to follow it. Since your stored data is valid base64, the affected sessions should load again once a fix like that ships. Your ideas about enforcing limits when a file is uploaded, out-of-band storage (see #46173) and reporting decode failures per attachment are worth raising on #45558 or #51272. If your problem turns out to be different, feel free to reply here.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions