thedotmack/claude-mem · error · Error
projection_error: one operation exceeds projection request…
Error message
projection_error: one operation exceeds projection request byte budget
What it means
getProjectionPage() batches ops subject to a byte budget (maxBytes, capped at PROJECTION_PAGE_MAX_BYTES). If a single serialized operation alone exceeds that budget — before any op has been accumulated — no valid page can be produced, so the DO throws this projectionError instead of returning an oversized request.
Solutions
- Raise maxBytes (and confirm it is bytes, not ops) so it comfortably exceeds the largest op in the range; the DO caps it at PROJECTION_PAGE_MAX_BYTES, so if the op exceeds the server cap the cap itself must be raised in config/code.
- Increase PROJECTION_PAGE_MAX_OPS/PAGE byte constants in the hub if large ops are legitimate for your workload.
- Split overly large client mutations at write time so no single canonical op exceeds the projection byte budget.
- Catch this error and route the offending seq to a dead-letter/splitting path instead of retrying the same page forever.
Example fix
// before const page = hub.getProjectionPage(token, target, userId, 500, 1024); // after const page = hub.getProjectionPage(token, target, userId, 500, PROJECTION_PAGE_MAX_BYTES);
Defensive patterns
Strategy: validation
Validate before calling
const byteLimit = Math.min(requestedBytes, PROJECTION_PAGE_MAX_BYTES);
if (largestKnownOpBytes > byteLimit) {
throw new Error(`op too large for projection budget: ${largestKnownOpBytes} > ${byteLimit}`);
} Try / catch
try {
page = hub.getProjectionPage(token, target, userId, maxOps, maxBytes);
} catch (e) {
if (String(e).includes('byte budget')) {
// raise maxBytes to server cap or split/skip the oversized op
page = hub.getProjectionPage(token, target, userId, maxOps, PROJECTION_PAGE_MAX_BYTES);
} else throw e;
} Prevention
- Remember maxBytes is bytes, not op count
- Cap client mutation sizes at write time below PROJECTION_PAGE_MAX_BYTES
- Alert when a single op approaches the byte limit
When it happens
Trigger: Calling getProjectionPage with maxBytes set very low (e.g. 1 or a tiny value) while the log contains an op larger than that budget; or a single canonical op genuinely larger than PROJECTION_PAGE_MAX_BYTES.
Common situations: Operator passes maxBytes as a small number intending 'few ops' but the unit is bytes; a legitimately huge op (e.g. a large document mutation) landed in the log and now no projection page can pass over it with default limits.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- projection_error: checkpoint compare-and-set mismatch
- projection_error: epoch mismatch
- projection_error: invalid projected through_seq
- projection_error: projection lease is not held
- projection_error: target_seq exceeds head_seq
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/0e395e1e0f26d699.
Report an issue: GitHub.
Appendix: source
Thrown at workers/sync-hub/src/do/SyncHub.ts:715
projected,
target,
target,
target,
limit,
).toArray();
const ops: ChangeOp[] = [];
for (const row of rows) {
const op = toChange(row);
const candidate = [...ops, op];
const bytes = projectionRequestBytes({
userId,
epoch,
fromSeqExclusive: projected,
throughSeq: op.seq,
ops: candidate,
});
if (bytes > byteLimit) {
if (ops.length === 0) throw projectionError("one operation exceeds projection request byte budget");
break;
}
ops.push(op);
}
if (ops.length === 0 && compareCanonicalDecimals(projected, target) < 0) {
throw projectionError("unprojected log gap");
}
this.renewProjectionLease(leaseToken, now);
return {
protocol_version: 1,
epoch,
from_seq_exclusive: projected,
through_seq: ops.length > 0 ? ops[ops.length - 1].seq : projected,
target_seq: target,
ops,
};
}
View on GitHub (pinned to d8bc9755e7)