can1357/oh-my-pi · error · AIError.ProviderResponseError
Devin Connect frame length ${len} exceeds ${MAX_CONNECT_FRAM
Error message
Devin Connect frame length ${len} exceeds ${MAX_CONNECT_FRAME_PAYLOAD}-byte cap What it means
This ProviderResponseError is thrown while parsing the Devin Connect binary stream in streamDevin. The wire protocol prefixes each frame with a 1-byte flag and a 4-byte big-endian length; before reading a frame body the parser checks the declared length against MAX_CONNECT_FRAME_PAYLOAD. A length over the cap means the byte stream is no longer a valid Connect framing sequence (desync or a non-Connect response), so continuing would allocate unbounded memory or misparse everything downstream.
Source
Thrown at packages/ai/src/providers/devin.ts:247
const reader = body.getReader();
let pending = Buffer.alloc(0);
for (;;) {
const { done, value } = await reader.read();
if (value && value.length > 0) {
// Steady state drains fully per chunk; view the fresh reader chunk
// instead of copying it through Buffer.concat (see aws-eventstream.ts).
pending =
pending.length === 0
? Buffer.from(value.buffer, value.byteOffset, value.byteLength)
: Buffer.concat([pending, value]);
}
while (pending.length >= 5) {
const flag = pending[0];
const len = pending.readUInt32BE(1);
if (len > MAX_CONNECT_FRAME_PAYLOAD) {
throw new AIError.ProviderResponseError(
`Devin Connect frame length ${len} exceeds ${MAX_CONNECT_FRAME_PAYLOAD}-byte cap`,
{ provider: model.provider, kind: "envelope" },
);
}
if (pending.length < 5 + len) break;
const payload = pending.subarray(5, 5 + len);
pending = pending.subarray(5 + len);
if (flag & CONNECT_END_STREAM_FLAG) {
const trailerBytes = flag & CONNECT_COMPRESSED_FLAG ? gunzipSync(payload) : payload;
const trailerError = readConnectTrailerError(trailerBytes.toString("utf8").trim());
if (trailerError) {
// #4218: these rejections carry no HTTP error body, so the raw
// trailer is the only server-side evidence. Log it with the
// request shape before classification discards it.
logger.warn("devin: stream rejected via Connect trailer", {
model: model.id,
code: trailerError.code,View on GitHub (pinned to 9690622007)
Solutions
- Verify the Devin base URL points at the Connect streaming endpoint, not a plain HTTP/JSON route
- Retry once — transient proxy corruption can desync framing; check whether the error recurs on every request
- Capture the model/provider config and update the pi-ai package if Devin changed its Connect framing protocol
- Check any corporate proxy/TLS-inspecting middlebox for body rewriting
Example fix
// before: pointing baseUrl at a JSON REST endpoint baseUrl: "https://api.devin.ai/v1" // after: use the Connect streaming endpoint for streamDevin baseUrl: "https://connect.devin.ai" // per Devin Connect docs
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the endpoint speaks Connect framing before streaming:
const res = await fetch(baseUrl + "/devin.Connect/Stream", { method: "POST" });
const ct = res.headers.get("content-type") ?? "";
if (!ct.includes("connect") && !ct.includes("proto")) {
throw new Error(`Expected Connect stream, got content-type ${ct}`);
} Type guard
function isConnectFrameHeader(buf: Uint8Array): boolean {
if (buf.length < 5) return false;
const len = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(1, false);
return len > 0 && len <= 4 * 1024 * 1024; // within sane frame cap
} Try / catch
try {
await streamDevin(model, context, options);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.message.includes("-byte cap")) {
// framing desync: surface the provider config and retry once or report a protocol bug
logger.error("Devin Connect framing desync", { model: model.id, cause: err });
} else throw err;
} Prevention
- Point baseUrl strictly at the Connect streaming endpoint, never a JSON/HTML route
- Keep pi-ai updated when Devin changes protocol versions
- Watch for proxies/TLS-inspection middleboxes that rewrite response bodies
- Log first bytes of unexpected failures to diagnose desync early
When it happens
Trigger: A streamed response from the Devin endpoint whose first five bytes decode to a frame length larger than MAX_CONNECT_FRAME_PAYLOAD — e.g. the server returned an HTML error page, JSON, or compressed/unframed data that the parser misread as a length header, or protocol/version drift changed the framing.
Common situations: Devin gateway changes or proxies returning HTML/JSON error bodies over HTTP 200; base URL pointing at a non-Connect endpoint; Devin server-side protocol changes; corrupt intermediate proxy mangling the byte stream.
Related errors
- Received text_delta for non-text content
- Received text_end for non-text content
- Received thinking_delta for non-thinking content
- Received thinking_end for non-thinking content
- Received toolcall_delta for non-toolCall content
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e6bfd6dd552caeaa.
Report an issue: GitHub.