ruvnet/ruflo · error · Error
${label} must be a canonical unsigned decimal integer
Error message
${label} must be a canonical unsigned decimal integer What it means
parseCanonicalUnsigned() accepts exactly one spelling of an unsigned integer: /^(?:0|[1-9][0-9]*)$/. It rejects leading zeros, '+', '0x' hex, underscores, surrounding whitespace, empty strings, and negatives. It is used for protocol fields such as inbox message sequence/cursor and fenced-lease version, where non-canonical spellings would break equality and ordering comparisons.
Source
Thrown at v3/@claude-flow/codex/src/harness/unsigned-integer.ts:7
const CANONICAL_UNSIGNED = /^(?:0|[1-9][0-9]*)$/;
const MAX_UNSIGNED_64 = (1n << 64n) - 1n;
/** Parse a decimal unsigned integer without accepting aliases such as +1, 01, hex, or whitespace. */
export function parseCanonicalUnsigned(value: string, label: string): bigint {
if (!CANONICAL_UNSIGNED.test(value)) {
throw new Error(`${label} must be a canonical unsigned decimal integer`);
}
const parsed = BigInt(value);
if (parsed > MAX_UNSIGNED_64) {
throw new Error(`${label} exceeds unsigned 64-bit range`);
}
return parsed;
}
View on GitHub (pinned to fa13ee4ad6)
Solutions
- Check the label in the message ('message sequence', 'inbox cursor', 'lease version') to find the offending field
- Emit plain decimal via BigInt.prototype.toString() or String(Number) and never pad or sign values
- Round-trip test your serializer against /^(?:0|[1-9][0-9]*)$/ before sending
Example fix
// before cursor: String(seq).padStart(4, '0'); // '0042' -> throws // after cursor: BigInt(seq).toString(); // '42'
Defensive patterns
Strategy: type-guard
Validate before calling
const CANONICAL = /^(?:0|[1-9][0-9]*)$/;
function isCanonicalUnsigned(v: string): boolean { return CANONICAL.test(v); } Type guard
const CANONICAL_UNSIGNED = /^(?:0|[1-9][0-9]*)$/;
function isCanonicalUnsigned(v: unknown): v is string {
return typeof v === 'string' && CANONICAL_UNSIGNED.test(v);
} Try / catch
try { parseCanonicalUnsigned(cursor, 'inbox cursor'); } catch (e) { if (/canonical unsigned/.test(String(e))) return rejectPeer('non-canonical integer field'); throw e; } Prevention
- Always serialize these fields with BigInt(x).toString() or String(Number(x))
- Property-test your serializer: output must match /^(?:0|[1-9][0-9]*)$/
- Never pad, sign, or localize protocol numbers
When it happens
Trigger: Passing '01', '+1', ' 1', '0x10', '1_000', '-5', '' or 1e21-derived strings as message.sequence, inbox cursor, or lease version; JSON codecs that reformat numbers (padding, exponent form) feed these fields.
Common situations: A peer implementation serializes integers with a different formatter; a database or JSON store returns '01'; hand-crafted test fixtures use padded strings; values were concatenated instead of parsed then re-stringified.
Related errors
- ${label} exceeds unsigned 64-bit range
- Validation failed: ${result.error}
- FORBIDDEN_PROTOCOL
- Rating must be integer 1-5
- localCompute: no adapter for graphId=${input.graphId}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/31777c5c2b8ca096.
Report an issue: GitHub.