can1357/oh-my-pi · error · Error
Expected boolean, got ${typeof value}
Error message
Expected boolean, got ${typeof value} What it means
Thrown by requireBoolean, the validator wired into the `bool` scalar codec. `scalar()` calls `validate(value)` on every encode, toJson, and isDefault call, so this fires whenever an application-supplied field value that the descriptor declares as `kind: "bool"` is not a JavaScript `true`/`false` at runtime. This is an application-side type error, not a wire-format problem: the reader path never produces it because `reader.bool()` always yields a boolean. The message includes the offending value's `typeof` (e.g. "string", "number", "undefined") to pinpoint the mismatch.
Source
Thrown at packages/catalog/src/discovery/protobuf.ts:636
defaultValue: undefined,
encode(value, writer) {
writer.lengthDelimited(getCodec().encode(value));
},
decode(reader) {
return getCodec().decode(reader.bytes());
},
toJson(value) {
return getCodec().toJson(value);
},
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;View on GitHub (pinned to 9690622007)
Solutions
- Coerce the value before building the message: `value === true || value === "true" || value === 1` (or `Boolean(value)` if truthiness semantics are acceptable).
- Build messages with `codec.create({...})` so defaults fill omitted fields, and let TypeScript's inferred message type catch wrong types at compile time instead of `as any`.
- Check the descriptor: if the field genuinely carries 0/1 or "true"/"false", change its ScalarKind (e.g. to "int32" or "string") rather than feeding mismatched data.
- Add a boundary validation step that normalizes incoming config/JSON to strict booleans before constructing protobuf messages.
Example fix
// before
const msg = Msg.create({ enabled: opts.enabled as any }); // opts.enabled = "true"
// after
const msg = Msg.create({ enabled: opts.enabled === "true" || opts.enabled === true }); Defensive patterns
Strategy: validation
Validate before calling
function isBool(v: unknown): v is boolean {
return typeof v === "boolean";
}
// before encode: if (!isBool(input.enabled)) throw new TypeError("enabled must be boolean"); Type guard
function isBoolean(v: unknown): v is boolean {
return typeof v === "boolean";
} Try / catch
try {
return MyMsg.encode(value);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Expected boolean, got ")) {
throw new TypeError(`field '${fieldName}' requires true/false; got: ${err.message}`);
}
throw err;
} Prevention
- Build messages exclusively through codec.create() so the inferred TypeScript type rejects non-booleans at compile time.
- Coerce config/form inputs ("true"/1) to strict booleans at the ingestion boundary.
- Avoid `as any`/type assertions on protobuf message fields.
- Match the descriptor ScalarKind to the real data type; don't feed 0/1 flags into "bool" fields unconverted.
When it happens
Trigger: Specific: calling `codec.encode(msg)`/`codec(msg)` where a bool field holds a string "true", a number 1/0, null, or undefined for a non-optional field; passing a protobuf-JSON-style value (where booleans are real booleans but you converted them through a parser that stringified them) into encode; calling `toJson`/`isDefault` on a hand-assembled message object that skipped `codec.create()` and set fields loosely; a descriptor that declares `"bool"` for a field the code populates with an enum number or int flag.
Common situations: Real-world: form/config values read as strings ("true"/"false") passed straight into the message; JSON.parse output reused across schemas where the field changed from bool to string; migration from another protobuf library whose types were looser (`boolean | 0 | 1`); TypeScript type assertions (`as any`) hiding the mismatch until runtime.
Related errors
- Expected Uint8Array
- Expected number, got ${typeof value}
- Expected int32, got ${typeof value}
- Unsupported protobuf wire type ${wireType} at byte ${reader.
- Unsupported wire type ${entryWireType} in map entry
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7cda9d793139ec5e.
Report an issue: GitHub.