can1357/oh-my-pi · error · Error
Sealed frame too short
Error message
Sealed frame too short
What it means
open() decrypts a sealed collaboration frame, which is laid out as IV || ciphertext. A frame must be strictly longer than the IV (IV_LENGTH bytes) to contain any ciphertext; anything shorter is malformed (truncated in transit or never produced by seal). Throwing early avoids a confusing WebCrypto 'operation error' from a zero-length ciphertext.
Source
Thrown at packages/coding-agent/src/collab/crypto.ts:48
}
return crypto.subtle.importKey("raw", asStrict(raw), AES_ALGORITHM, false, ["encrypt", "decrypt"]);
}
export async function seal(key: CryptoKey, frame: CollabFrame): Promise<Uint8Array> {
const iv = new Uint8Array(IV_LENGTH);
crypto.getRandomValues(iv);
const plaintext = TEXT_ENCODER.encode(JSON.stringify(frame));
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: AES_ALGORITHM, iv }, key, plaintext));
const out = new Uint8Array(IV_LENGTH + ciphertext.byteLength);
out.set(iv, 0);
out.set(ciphertext, IV_LENGTH);
return out;
}
/** Inverse of {@link seal}. Throws on auth failure or malformed input. */
export async function open(key: CryptoKey, data: Uint8Array): Promise<CollabFrame> {
if (data.byteLength <= IV_LENGTH) {
throw new Error("Sealed frame too short");
}
const iv = asStrict(data.subarray(0, IV_LENGTH));
const ciphertext = asStrict(data.subarray(IV_LENGTH));
const plaintext = new Uint8Array(await crypto.subtle.decrypt({ name: AES_ALGORITHM, iv }, key, ciphertext));
return JSON.parse(TEXT_DECODER.decode(plaintext)) as CollabFrame;
}
function asStrict(bytes: Uint8Array): Uint8Array<ArrayBuffer> {
if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
return bytes as Uint8Array<ArrayBuffer>;
}
const copy = new Uint8Array(bytes.byteLength);
copy.set(bytes);
return copy;
}
View on GitHub (pinned to 9690622007)
Solutions
- Verify the websocket message is a binary frame from the peer before calling open; skip/handle text frames separately.
- Check data.byteLength > IV_LENGTH at the receive site and drop/ignore short frames as corrupt.
- Confirm both peers run compatible versions of the collab protocol and that no intermediary truncates binary messages.
- If frames are stored/relayed, ensure the full sealed buffer is transmitted (no slicing off the IV prefix).
Example fix
// before const frame = await open(key, new Uint8Array(await msg.arrayBuffer())); // after const bytes = new Uint8Array(await msg.arrayBuffer()); if (bytes.byteLength <= IV_LENGTH) return; // ignore corrupt frame const frame = await open(key, bytes);
Defensive patterns
Strategy: validation
Validate before calling
if (!(data instanceof Uint8Array) || data.byteLength <= IV_LENGTH) return; // skip corrupt/empty frame const frame = await open(key, data);
Type guard
function isOpenableFrame(data: unknown): data is Uint8Array {
return data instanceof Uint8Array && data.byteLength > IV_LENGTH;
} Try / catch
try {
const frame = await open(key, data);
} catch {
// treat as corrupt frame: log and ignore; peers authenticate via GCM anyway
} Prevention
- Route only binary websocket frames into open(); handle text frames separately.
- Never strip or re-encode the IV prefix when relaying frames.
- Keep collab protocol versions in sync across host and guests.
When it happens
Trigger: Calling open(key, data) with data.byteLength <= IV_LENGTH: a relay delivers an empty or whitespace-only binary message, a peer's message was truncated by a proxy, or the caller passes raw JSON/plaintext text instead of the sealed binary frame.
Common situations: A flaky relay or load balancer drops the tail of a binary websocket message; a custom message handler feeds text frames (or JSON) into open() instead of binary frames; a version mismatch where one peer seals frames with a different layout; tests feed empty Uint8Array buffers.
Related errors
- Replacement text is not valid UTF-8: {err}
- invalid glob `{pattern}`: {error}
- Cannot continue from message role: assistant
- V2 compaction expected exactly one compaction output item, g
- Received text_delta for non-text content
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/827c141266992f66.
Report an issue: GitHub.