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

  1. 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)
  2. Pre-validate with /^(?:0|[1-9][0-9]*)$/ (no leading zeros) and BigInt(cursor) <= 2n**64n-1n before calling receive
  3. Store and transmit cursors as strings end-to-end; never round-trip through Number
  4. 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

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


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/4a32fc1cfc7e665a. Report an issue: GitHub.