can1357/oh-my-pi · error · Error

Expected int32, got ${typeof value}

Error message

Expected int32, got ${typeof value}

What it means

Thrown by requireInt32, the validator for the `int32` and `enum` scalar kinds. It requires a JavaScript number that is an integer (`Number.isInteger`), then narrows it with `value | 0` to the signed 32-bit range on the way out. Non-integers (1.5), non-numbers (string "2", bigint 2n, null, undefined), and implicitly non-integer edge values (NaN) all throw with the value's `typeof`. Note the validator checks integrality, not the ±2^31 range — an out-of-range integer like 3_000_000_000 passes the check but is silently truncated by `| 0`, so range enforcement is on the caller.

Source

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

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}`);
}

function requireUint32(value: unknown): number {
	if (typeof value === "number" && Number.isInteger(value) && value >= 0) return value >>> 0;
	throw new Error(`Expected uint32, got ${typeof value}`);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Map string enum names to their numeric values before encoding (keep a name->number lookup table for your enum).
  2. Round explicitly with `Math.round`/`Math.trunc` when a computed value must be integral, and assert range `-2147483648 <= n <= 2147483647`.
  3. Convert bigints with `Number(big)` after verifying it fits in int32; use "int64"/"uint64" kinds when it doesn't.
  4. Parse numeric strings with `Number.parseInt(s, 10)` and validate before building the message.
  5. Use codec.create() with the inferred TypeScript types so wrong field types fail at compile time.

Example fix

// before
const msg = Task.create({ status: body.status }); // body.status = "DONE" (string)
// after
const status = STATUS_BY_NAME[body.status]; // { TODO: 0, DONE: 1, ... }
if (status === undefined) throw new TypeError(`unknown status: ${body.status}`);
const msg = Task.create({ status });
Defensive patterns

Strategy: validation

Validate before calling

function toInt32(v: unknown): number {
  const n = typeof v === "string" ? Number.parseInt(v, 10) : typeof v === "bigint" ? Number(v) : v;
  if (typeof n !== "number" || !Number.isInteger(n) || n < -2147483648 || n > 2147483647) {
    throw new TypeError(`int32/enum field needs an integer in [-2^31, 2^31), got ${String(v)}`);
  }
  return n;
}

Type guard

function isInt32(v: unknown): v is number {
  return typeof v === "number" && Number.isInteger(v) && v >= -2147483648 && v <= 2147483647;
}

Try / catch

try {
  return MyMsg.encode(value);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Expected int32, got ")) {
    throw new TypeError(`int32/enum field '${fieldName}' got ${err.message}; map string enum names to numbers and round computed values first`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Specific: encoding an int32 or enum field with a fractional number (computed averages, divisions); passing a string enum name ("ACTIVE") where the descriptor expects the numeric enum value; passing a bigint from a database or 64-bit source into an int32 field; undefined for a required field; NaN from failed parsing; feeding a float-typed value into an enum field after a descriptor refactor.

Common situations: Real-world: enum values fetched from an API as strings; Math.mean-style results stored without rounding; DB drivers returning bigint for counts; TypeScript enum objects iterated as mixed keys/values and the string key passed through; legacy code that used numeric strings in JSON payloads.

Related errors


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