koala73/worldmonitor · error · Error
[_persistPickedBatch] chunk too large: ${contacts.length} >
Error message
[_persistPickedBatch] chunk too large: ${contacts.length} > ${PERSIST_CHUNK_SIZE} What it means
Thrown by _persistPickedBatch when the contacts array length exceeds PERSIST_CHUNK_SIZE (defined as 500 in waveRuns.ts:77). The mutation exists specifically to stay under Convex's per-mutation write limit, so it refuses oversized chunks rather than attempting a write that Convex would reject. The caller (pickWaveAction) is responsible for slicing into 500-row chunks.
Source
Thrown at convex/broadcast/waveRuns.ts:564
createdAt: now,
updatedAt: now,
});
return { ok: true, runId: args.runId };
},
});
/**
* Insert a chunk of picked-contact rows. Called repeatedly from
* `pickWaveAction` to stay under Convex per-mutation write limits.
*/
export const _persistPickedBatch = internalMutation({
args: {
runId: v.string(),
contacts: v.array(v.string()), // normalizedEmails
},
handler: async (ctx, { runId, contacts }) => {
if (contacts.length > PERSIST_CHUNK_SIZE) {
throw new Error(
`[_persistPickedBatch] chunk too large: ${contacts.length} > ${PERSIST_CHUNK_SIZE}`,
);
}
const now = Date.now();
for (const email of contacts) {
await ctx.db.insert("wavePickedContacts", {
runId,
normalizedEmail: email,
status: "pending",
});
}
// Bump updatedAt so the in-flight guard's lastActivityAt fallback sees fresh activity.
const run = await ctx.db
.query("waveRuns")
.withIndex("by_runId", (q) => q.eq("runId", runId))
.unique();
if (run) await ctx.db.patch(run._id, { updatedAt: now });
return { inserted: contacts.length };View on GitHub (pinned to ffec79ac33)
Solutions
- Ensure the caller slices contacts into chunks of at most PERSIST_CHUNK_SIZE (500): `for (let i = 0; i < picked.length; i += PERSIST_CHUNK_SIZE) { const chunk = picked.slice(i, i + PERSIST_CHUNK_SIZE); await runMutation(..., {contacts: chunk}); }`.
- If you intentionally changed PERSIST_CHUNK_SIZE, update every call site that slices by it.
- Never call _persistPickedBatch with an unsliced array from a new action.
Example fix
// before — caller passes unsliced batch
await ctx.runMutation(internal.broadcast.waveRuns._persistPickedBatch, { runId, contacts: picked });
// after — caller slices into 500-row chunks
const PERSIST_CHUNK_SIZE = 500;
for (let i = 0; i < picked.length; i += PERSIST_CHUNK_SIZE) {
const chunk = picked.slice(i, i + PERSIST_CHUNK_SIZE);
await ctx.runMutation(internal.broadcast.waveRuns._persistPickedBatch, { runId, contacts: chunk });
} Defensive patterns
Strategy: validation
Validate before calling
const PERSIST_CHUNK_SIZE = 500;
function assertChunkSize(contacts: string[]): void {
if (contacts.length > PERSIST_CHUNK_SIZE) {
throw new Error(`Batch too large: ${contacts.length} > ${PERSIST_CHUNK_SIZE}. Slice the caller's array.`);
}
}
// slice before calling: for (let i = 0; i < picked.length; i += PERSIST_CHUNK_SIZE) { ... } Prevention
- Always slice contacts into PERSIST_CHUNK_SIZE (500) chunks before calling _persistPickedBatch.
- Import PERSIST_CHUNK_SIZE from the same module rather than hardcoding the number in the caller.
- Never call _persistPickedBatch from a new code path without first checking the batch size.
When it happens
Trigger: pickWaveAction calls runMutation('_persistPickedBatch', {contacts}) with an array longer than 500 because the slicing loop (waveRuns.ts:869-870) was modified or bypassed. A different caller invokes _persistPickedBatch directly with an unsliced batch.
Common situations: A developer changes PERSIST_CHUNK_SIZE to a smaller number but forgets to update the slicing loop, or vice versa. A new code path calls _persistPickedBatch without slicing. A test passes a large fixture array directly.
Related errors
- [_recordPoolFilterStats] no run ${args.runId}
- [_markPickComplete] no run ${args.runId}
- [_markPickComplete] run ${args.runId} is ${run.status}, expe
- PRO_REQUIRED
- INCOMPATIBLE_DELIVERY
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/f9fad5ed9c5b49da.
Report an issue: GitHub.