{"record":{"id":"450f049523e5a4bd","repo":"can1357/oh-my-pi","slug":"session-too-large-to-share-sealed-bytelength-b","errorCode":null,"errorMessage":"Session too large to share: ${sealed.byteLength} bytes sealed exceeds the ${maxBytes} byte limit","messagePattern":"Session too large to share: (.+?) bytes sealed exceeds the (.+?) byte limit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/export/share.ts","lineNumber":548,"sourceCode":"\tconst working = structuredClone(data);\n\tstripImagePayloads(working);\n\tsealed = await sealSessionData(key, working);\n\tif (sealed.byteLength <= maxBytes) return { sealed, truncated: true };\n\n\tfor (const cap of TEXT_CAPS) {\n\t\tcapLongStrings(working, cap);\n\t\tsealed = await sealSessionData(key, working);\n\t\tif (sealed.byteLength <= maxBytes) return { sealed, truncated: true };\n\t}\n\n\t// Last resort: drop oldest entries (orphaned children render as roots).\n\twhile (working.entries.length > 4) {\n\t\tworking.entries = working.entries.slice(Math.ceil(working.entries.length / 2));\n\t\tsealed = await sealSessionData(key, working);\n\t\tif (sealed.byteLength <= maxBytes) return { sealed, truncated: true };\n\t}\n\n\tthrow new Error(`Session too large to share: ${sealed.byteLength} bytes sealed exceeds the ${maxBytes} byte limit`);\n}\n\n/** `[12B IV][AES-256-GCM(gzip(JSON))]` — decrypted and gunzipped by share-loader.js. */\nasync function sealSessionData(key: CryptoKey, data: SessionData): Promise<Uint8Array<ArrayBuffer>> {\n\tconst compressed = Bun.gzipSync(new TextEncoder().encode(JSON.stringify(data)));\n\tconst iv = new Uint8Array(IV_LENGTH);\n\tcrypto.getRandomValues(iv);\n\tconst ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: \"AES-GCM\", iv }, key, compressed));\n\tconst out = new Uint8Array(IV_LENGTH + ciphertext.byteLength);\n\tout.set(iv, 0);\n\tout.set(ciphertext, IV_LENGTH);\n\treturn out;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/export/share.ts#L530-L566","documentation":"sealToFit encrypts a session for sharing and progressively shrinks it (strip images, cap long strings, halve the entry list down to a floor of 4 entries) until the AES-GCM sealed blob fits maxBytes. If even the minimal payload (at most 4 entries, truncated strings, no images) still encrypts larger than maxBytes, the function gives up and throws this error rather than produce an undeliverable share.","triggerScenarios":"Calling forGist/forServer/sealToFit on a session whose remaining content exceeds the share byte budget even after image stripping, string capping, and entry halving to the 4-entry floor — e.g. 4 entries each containing megabytes of pasted text against a small maxBytes budget.","commonSituations":"Sharing an extremely long session with giant tool outputs or pasted logs; sharing over a host that enforces a tight upload limit (GitHub Gist limits, a self-hosted share server with a small body cap); running an older share client against a server whose limit was lowered.","solutions":["Reduce the session content before sharing: start a fresh session or delete/clear the largest entries (huge tool outputs, pasted files) and retry the share.","Raise or check the byte budget: if you control the call site, pass a larger maxBytes to sealToFit / configure the share server limit appropriately.","Re-run the share after compacting: the error message reports the actual sealed size vs the limit, so trim content until it fits (e.g. remove the remaining oversized entries).","If the target is a gist, use the server share path or vice versa — different hosts have different limits."],"exampleFix":"// before\nawait share.forServer(session, { maxBytes: 512 * 1024 }); // session has 4 x 500KB pasted logs\n// after\n// trim the session first (drop huge tool outputs), or raise the budget:\nawait share.forServer(session, { maxBytes: 8 * 1024 * 1024 });","handlingStrategy":"validation","validationCode":"// Rough pre-check: estimate JSON size before sharing\nconst approx = new TextEncoder().encode(JSON.stringify(sessionData)).byteLength;\nif (approx > maxBytes) {\n  // trim entries/tool outputs first, or pick a host with a bigger limit\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { sealed, truncated } = await sealToFit(key, data, maxBytes);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Session too large to share')) {\n    // fall back to local export or prompt user to trim the session\n  } else throw err;\n}","preventionTips":["Prune large tool outputs and pasted files from sessions before sharing","Check session size before share and warn the user early","Know each host's byte limit (gist vs share server) and choose accordingly","Keep maxBytes budgets in one config place so they can be raised"],"tags":["session","size-limit","sharing","encryption"],"backgroundTag":"payload-size-limit-exceeded","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}