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
- Map string enum names to their numeric values before encoding (keep a name->number lookup table for your enum).
- Round explicitly with `Math.round`/`Math.trunc` when a computed value must be integral, and assert range `-2147483648 <= n <= 2147483647`.
- Convert bigints with `Number(big)` after verifying it fits in int32; use "int64"/"uint64" kinds when it doesn't.
- Parse numeric strings with `Number.parseInt(s, 10)` and validate before building the message.
- 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
- Maintain name->number lookup tables for enums and never encode raw API string values.
- Round or truncate computed values (Math.trunc) before assigning to int32 fields.
- Check the ±2^31 range yourself — the validator's `| 0` silently truncates out-of-range integers that pass the integrality check.
- Use codec.create() with inferred TypeScript enum types so wrong enum inputs fail at compile time.
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
- Expected boolean, got ${typeof value}
- Expected Uint8Array
- Expected number, got ${typeof value}
- Unsupported language '{value}'. Supported: {}
- Unsupported protobuf wire type ${wireType} at byte ${reader.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/865247e01bd484e6.
Report an issue: GitHub.