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

  1. Coerce the value before building the message: `value === true || value === "true" || value === 1` (or `Boolean(value)` if truthiness semantics are acceptable).
  2. 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`.
  3. 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.
  4. 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

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


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