ruvnet/ruflo · error · Error
inbox cursor must be a non-negative decimal integer
Error message
inbox cursor must be a non-negative decimal integer
What it means
Thrown by InMemoryHarnessInbox.receive(audience, cursor) when the resume cursor fails parseCanonicalUnsigned. The inbox stamps each message with strictly increasing decimal cursors ('1', '2', ...) from an internal BigInt counter, and resumes only from strings matching /^(?:0|[1-9][0-9]*)$/ within unsigned 64-bit range: no sign, no leading zeros ('01'), no whitespace, hex, or floats. The catch block discards the specific parse error, so both format violations and u64 overflow surface as this one generic message.
Source
Thrown at v3/@claude-flow/codex/src/harness/in-memory-inbox-reference.ts:158
const record = matches[0]!;
if (record.acknowledgedAt === undefined) record.acknowledgedAt = new Date(this.now()).toISOString();
}
pending(audience: string): InMemoryInboxRecord[] {
return this.records
.filter((record) => record.message.audience === audience && record.acknowledgedAt === undefined)
.map(clone);
}
quarantineRecords(): InMemoryQuarantinedMessage[] {
return this.quarantined.map(clone);
}
private parseCursor(cursor: string): bigint {
try {
return parseCanonicalUnsigned(cursor, 'inbox cursor');
} catch {
throw new Error('inbox cursor must be a non-negative decimal integer');
}
}
private quarantine(
message: HarnessMessage,
reason: InMemoryQuarantinedMessage['reason'],
quarantinedAt: string,
): void {
this.quarantined.push({
issuer: message.issuer,
messageId: message.messageId,
reason,
quarantinedAt,
});
}
}
View on GitHub (pinned to fa13ee4ad6)
Solutions
- Pass only the cursor field of a previously received HarnessMessage, or omit the argument entirely (it defaults to '0', resuming from the oldest unacknowledged message)
- Pre-validate with /^(?:0|[1-9][0-9]*)$/ (no leading zeros) and BigInt(cursor) <= 2n**64n-1n before calling receive
- Store and transmit cursors as strings end-to-end; never round-trip through Number
- To restart a consumer from the beginning, pass '0' explicitly rather than '' or null
Example fix
// before: lastOffset is a page counter, not a cursor
for await (const m of inbox.receive('worker', String(lastOffset))) { handle(m); }
// after: use the cursor stamped on the last processed message, or '0' to start fresh
const cursor = lastMessage ? lastMessage.cursor : '0';
for await (const m of inbox.receive('worker', cursor)) { handle(m); lastMessage = m; } Defensive patterns
Strategy: validation
Validate before calling
const CANONICAL_CURSOR = /^(?:0|[1-9][0-9]*)$/;
const MAX_U64 = (1n << 64n) - 1n;
function assertValidCursor(cursor: string): void {
if (!CANONICAL_CURSOR.test(cursor)) throw new TypeError(`invalid inbox cursor: ${JSON.stringify(cursor)}`);
if (BigInt(cursor) > MAX_U64) throw new RangeError('inbox cursor exceeds unsigned 64-bit range');
} Type guard
function isInboxCursor(value: unknown): value is string {
return typeof value === 'string'
&& /^(?:0|[1-9][0-9]*)$/.test(value)
&& BigInt(value) <= (1n << 64n) - 1n;
} Try / catch
try {
for await (const message of inbox.receive(audience, cursor)) { /* ... */ }
} catch (error) {
if (error instanceof Error && error.message === 'inbox cursor must be a non-negative decimal integer') {
// cursor is unrecoverable: log it and restart from '0' (oldest unacknowledged)
logger.warn('bad cursor, resetting', { cursor });
cursor = '0';
} else throw error;
} Prevention
- Always source the resume cursor from the cursor field of a received HarnessMessage
- Treat cursors as opaque canonical strings; never construct them from offsets, page numbers, or Numbers
- Pass '0' (or omit the parameter) to start from the oldest unacknowledged message
When it happens
Trigger: Calling inbox.receive('worker-1', cursor) with '-1', '1e3', '0x1f', '12.5', ' 42', '01', '', 'null', 'undefined', or a numeric string above 18446744073709551615. Also passing a page index, an array offset, or an opaque/base64 cursor copied from a different pagination system.
Common situations: Persisting the cursor as a JS number and re-stringifying it (floats/rounding corrupt it); building cursors from offsets instead of reading message.cursor; optional config where cursor is undefined and interpolated as the string 'undefined'; migrating from another API whose cursors are base64 blobs.
Related errors
- localCompute: no adapter for graphId=${input.graphId}
- Invalid completion type
- Router multimodal is enabled but LLM_ROUTER_MULTIMODAL_MODEL
- Routes config must be a flat array of routes
- Invalid route entry: ${JSON.stringify(r)}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/4a32fc1cfc7e665a.
Report an issue: GitHub.