agalwood/Motrix · error · RangeError

${label} must be non-negative

Error message

${label} must be non-negative

What it means

Thrown by nonNegativeIntegerFromBigInt after safeIntegerFromSql succeeds but the resulting number is negative. The helper enforces a non-negative contract (counts, sizes, rowids) on top of the safe-integer conversion.

Source

Thrown at src/core/lib/sqlite-integers.ts:31

  if (typeof value !== 'bigint') {
    throw new RangeError(`${label} is not an integer`)
  }
  if (
    value < BigInt(Number.MIN_SAFE_INTEGER) ||
    value > BigInt(Number.MAX_SAFE_INTEGER)
  ) {
    throw new RangeError(`${label} exceeds the JavaScript safe integer range`)
  }
  return Number(value)
}

export function nonNegativeIntegerFromBigInt(
  value: bigint,
  label: string
): number {
  const converted = safeIntegerFromSql(value, label)
  if (converted < 0) {
    throw new RangeError(`${label} must be non-negative`)
  }
  return converted
}

export function requireSafeTimestamp(value: number, label: string): void {
  if (!Number.isSafeInteger(value)) {
    throw new RangeError(`${label} must be a safe integer timestamp`)
  }
}

export function requireSafePositiveTimestamp(
  value: number,
  label: string
): void {
  if (!Number.isSafeInteger(value) || value <= 0) {
    throw new RangeError(`${label} must be a positive safe integer timestamp`)
  }
}

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Inspect the row that triggered it and find the writer that produced the negative value.
  2. Add a CHECK (col >= 0) constraint at the SQLite schema level to prevent future bad writes.
  3. If negatives are valid for this field, call safeIntegerFromSql directly instead of the non-negative variant.
  4. Clamp at the write site: UPDATE ... SET col = MAX(0, col - ?).

Example fix

// before
const count = nonNegativeIntegerFromBigInt(row.delta, 'delta')
// after — field is genuinely signed
const delta = safeIntegerFromSql(row.delta, 'delta')
Defensive patterns

Strategy: validation

Validate before calling

function isNonNegativeSafeInteger(value: bigint): boolean {
  return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER)
}
if (!isNonNegativeSafeInteger(row.count)) {
  throw new Error(`count out of range: ${row.count}`)
}

Type guard

function isNonNegativeSafe(value: unknown): value is number {
  if (typeof value === 'bigint') return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER)
  if (typeof value === 'number') return Number.isSafeInteger(value) && value >= 0
  return false
}

Try / catch

try {
  const count = nonNegativeIntegerFromBigInt(row.count, 'count')
} catch (e) {
  if (e instanceof RangeError && /non-negative/.test(e.message)) {
    // log and clamp or reject the row
  } else throw e
}

Prevention

When it happens

Trigger: Calling nonNegativeIntegerFromBigInt(value, label) where value is a negative bigint or a negative in-range number; e.g. a column storing a signed delta that legitimately went below zero, or a corrupted rowid.

Common situations: A count/size/offset column that received a negative value due to a buggy increment; signed-vs-unsigned mismatch when importing data; underflow in a decrement operation that was written back to the DB.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/01ea81f978dfd04f. Report an issue: GitHub.