can1357/oh-my-pi · error · Error

Expected number, got ${typeof value}

Error message

Expected number, got ${typeof value}

What it means

Thrown by requireNumber, the validator for the `double` and `float` scalar kinds. It accepts only finite JavaScript numbers (`typeof value === "number" && Number.isFinite(value)`); anything else — strings, bigint, NaN, Infinity, null, undefined — throws with the value's `typeof`. The finite check is deliberate: protobuf double/float fields have no NaN/Infinity in this codec's value domain, and 64/32-bit float encoding of non-finites would silently corrupt the stream. Fired from encode/toJson/isDefault paths; the decode path (reader.double/float) always returns finite numbers.

Source

Thrown at packages/catalog/src/discovery/protobuf.ts:646

		isDefault(value) {
			return value === undefined;
		},
	};
}

function requireBoolean(value: unknown): boolean {
	if (typeof value === "boolean") return value;
	throw new Error(`Expected boolean, got ${typeof value}`);
}

function requireBytes(value: unknown): Uint8Array {
	if (value instanceof Uint8Array) return value;
	throw new Error("Expected Uint8Array");
}

function requireNumber(value: unknown): number {
	if (typeof value === "number" && Number.isFinite(value)) return value;
	throw new Error(`Expected number, got ${typeof value}`);
}

function requireInt32(value: unknown): number {
	if (typeof value === "number" && Number.isInteger(value)) return value | 0;
	throw new Error(`Expected int32, got ${typeof value}`);
}
function requireString(value: unknown): string {
	if (typeof value === "string") return value;
	throw new Error(`Expected string, got ${typeof value}`);
}

function requireBigInt(value: unknown): bigint {
	if (typeof value === "bigint") return value;
	if (typeof value === "number" && Number.isInteger(value)) return BigInt(value);
	if (typeof value === "string") return BigInt(value);
	throw new Error(`Expected bigint, got ${typeof value}`);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Coerce at the boundary: `Number(value)` then guard with `Number.isFinite(n)` before constructing the message; reject or default non-finite results explicitly.
  2. Parse strings with `Number.parseFloat`/`Number` rather than passing them through.
  3. Convert bigints with `Number(bigint)` (mind precision loss) or change the field kind to "int64"/"uint64" if integer semantics matter.
  4. Use codec.create() so TypeScript's inferred partial type flags wrong types statically; avoid `as any`.

Example fix

// before
const msg = Metrics.create({ latencyMs: row.latency }); // row.latency: bigint | string
// after
const n = Number(row.latency);
if (!Number.isFinite(n)) throw new TypeError(`latency not finite: ${row.latency}`);
const msg = Metrics.create({ latencyMs: n });
Defensive patterns

Strategy: validation

Validate before calling

function toFiniteNumber(v: unknown): number {
  const n = typeof v === "bigint" ? Number(v) : typeof v === "string" ? Number(v) : v;
  if (typeof n !== "number" || !Number.isFinite(n)) {
    throw new TypeError(`float/double field needs a finite number, got ${String(v)}`);
  }
  return n;
}

Type guard

function isFiniteNumber(v: unknown): v is number {
  return typeof v === "number" && Number.isFinite(v);
}

Try / catch

try {
  return MyMsg.encode(value);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Expected number, got ")) {
    throw new TypeError(`numeric field '${fieldName}' got ${err.message}; coerce strings/bigints and reject NaN/Infinity first`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Specific: encoding a double/float field whose value came from a JSON parse that produced a string ("3.14") or null; passing a bigint (e.g. from a DB driver) into a float field; NaN or Infinity from an upstream division by zero or a placeholder in partial data; undefined for a required (non-optional) numeric field; calling toJson on a hand-built message without codec.create().

Common situations: Real-world: config/CSV/API inputs delivered as numeric strings; SQL drivers returning bigint for BIGINT columns mapped to float fields; sentinel values (Number.MAX_VALUE * 2, 1/0) leaking from computations; JSON Schema-validated data where "number" allowed strings in a lenient validator.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/cf13c08504ec275a. Report an issue: GitHub.