ruvnet/ruflo · error
build input bytes must be a non-negative safe integer
Error message
build input bytes must be a non-negative safe integer
What it means
Each DeclaredBuildInput.bytes value must satisfy Number.isSafeInteger(bytes) && bytes >= 0. Negative values, fractional values, NaN, Infinity, and integers beyond Number.MAX_SAFE_INTEGER (2^53-1) all throw when createBuildEvidence validates the folded input list, because the evidence contract must hash to identical bytes on every platform.
Source
Thrown at v3/@claude-flow/codex/src/harness/build-evidence.ts:120
export function createBuildEvidence(
sourceState: ExactSourceState,
buildInputs: readonly DeclaredBuildInput[],
toolchains: readonly DeclaredToolchain[],
): BuildEvidence {
const inputs = buildInputs.map((input) => ({
name: requireText(input.name, 'build input name'),
path: normalizePath(input.path),
digest: requireDigest(input.digest, 'build input digest'),
bytes: input.bytes,
})).sort((left, right) => compare(left.path, right.path) || compare(left.name, right.name));
const tools = toolchains.map((toolchain) => ({
name: requireText(toolchain.name, 'toolchain name'),
version: requireText(toolchain.version, 'toolchain version'),
digest: requireDigest(toolchain.digest, 'toolchain digest'),
})).sort((left, right) => compare(left.name, right.name) || compare(left.version, right.version));
if (inputs.some(({ bytes }) => !Number.isSafeInteger(bytes) || bytes < 0)) {
throw new Error('build input bytes must be a non-negative safe integer');
}
const inputKeys = inputs.map(({ name, path }) => `${name}\0${path}`);
if (new Set(inputKeys).size !== inputKeys.length) throw new Error('duplicate declared build input');
const foldedPaths = inputs.map(({ path }) => portableCaseFold(path));
if (new Set(foldedPaths).size !== foldedPaths.length) {
throw new Error('case-fold collision in declared build inputs');
}
const toolKeys = tools.map(({ name, version }) => `${name}\0${version}`);
if (new Set(toolKeys).size !== toolKeys.length) throw new Error('duplicate declared toolchain');
const body = {
contractVersion: 1 as const,
assurance: 'declared-unsigned' as const,
sourceStateId: requireDigest(sourceState.sourceStateId, 'source state id'),
buildInputs: inputs,
toolchains: tools,
};
return { ...body, evidenceDigest: sha256(canonicalJson(body)) };View on GitHub (pinned to fa13ee4ad6)
Solutions
- Audit every buildInputs[].bytes value — all must be whole numbers ≥ 0 and ≤ 2^53-1
- Fix the measurement source: use integer byte counts from fs stat (stat.size), not derived floats
- If a genuine input exceeds 2^53-1 bytes, split it or declare a manifest of it — the contract cannot represent that size
Example fix
// before bytes: stats.size / 1024, // fractional KB value bytes: -1, // after bytes: stats.size, // integer byte length from fs.Stats
Defensive patterns
Strategy: validation
Validate before calling
function isValidByteCount(bytes: unknown): bytes is number {
return typeof bytes === 'number' && Number.isSafeInteger(bytes) && bytes >= 0;
} Type guard
function isValidByteCount(bytes: unknown): bytes is number {
return typeof bytes === 'number' && Number.isSafeInteger(bytes) && bytes >= 0;
} Prevention
- Take sizes directly from fs.Stats.size (already a safe integer)
- Never derive bytes through division or averaging without Math.round and bounds checks
- Validate deserialized JSON size fields before they reach evidence creation
When it happens
Trigger: bytes set to -1; bytes computed as a float (averages, unit conversions, sizes divided by 1024.0); NaN propagated from parsing; a byte count larger than 2^53-1.
Common situations: Sizes measured by external tools and parsed from strings; BigInt file sizes converted with Number() losing precision; uninitialized size fields defaulting to NaN in generated declarations.
Related errors
- ${label} must be non-empty
- ${label} must be a canonical sha256 digest
- unsafe build input path: ${value}
- build evidence path is not a file or symlink: ${path}
- duplicate declared build input
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/0de45ddf4fa11b36.
Report an issue: GitHub.