can1357/oh-my-pi · error · Error

Unknown oneof field ${fieldNumber}

Error message

Unknown oneof field ${fieldNumber}

What it means

Thrown by the compiled oneof decoder in compileOneofField when an incoming protobuf message contains a tag whose field number does not map to any variant declared for that oneof. The oneof handler builds two maps from the descriptor (variantsByName, variantsByNumber) at compile time; during decode, `variantsByNumber.get(fieldNumber)` returned undefined, so the wire carries a field number this schema never declared. Unlike unknown top-level fields (which are captured in `$unknown` for forward-compatible round-trips), an unrecognized number inside the oneof's declared number range or one misrouted to the oneof handler is treated as a schema violation and thrown. It means the binary payload and the descriptor IR disagree.

Source

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

	return {
		number: 0,
		initDefault(message) {
			Reflect.set(message, name, { case: undefined });
		},
		encode(message, writer) {
			const oneof = Reflect.get(message, name);
			if (!oneof || typeof oneof !== "object" || !("case" in oneof) || typeof oneof.case !== "string") return;
			const variant = variantsByName.get(oneof.case);
			if (!variant) return;
			const value = Reflect.get(oneof, "value");
			if (value === undefined) return;
			writer.tag(variant.no, variant.codec.wireType);
			variant.codec.encode(value, writer);
		},
		decode(message, reader, wireType, fieldNumber) {
			const variant = variantsByNumber.get(fieldNumber);
			if (!variant) throw new Error(`Unknown oneof field ${fieldNumber}`);
			assertWireType(wireType, variant.codec.wireType);
			Reflect.set(message, name, { case: variant.name, value: variant.codec.decode(reader) });
		},
		toJson(message, output) {
			const oneof = Reflect.get(message, name);
			if (!oneof || typeof oneof !== "object" || !("case" in oneof) || typeof oneof.case !== "string") return;
			const variant = variantsByName.get(oneof.case);
			if (!variant) return;
			const value = Reflect.get(oneof, "value");
			if (value === undefined) return;
			output[oneof.case] = variant.codec.toJson(value);
		},
	};
}

function isPackableScalar(value: ValueCodec<unknown>): boolean {
	return value.wireType === 0 || value.wireType === 1 || value.wireType === 5;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Log the offending fieldNumber and compare it against the `no` values in the OneofFieldDesc — identify which variant is missing and add it to `desc.variants`.
  2. Regenerate or update the descriptor IR from the current .proto so sender and receiver agree on oneof field numbers.
  3. If the number should be treated as unknown rather than fatal, skip the field instead of throwing (mirror the top-level unknown-field handling that populates `$unknown`), but only if your contract allows dropping it.
  4. Verify the decoder dispatch isn't routing another field's tag to this oneof handler (off-by-one in the compiled field-number table).

Example fix

// before (descriptor missing the newer variant)
kind: "oneof",
name: "result",
variants: [
  { no: 1, name: "text", kind: "string" },
]
// after (add the arm the sender now emits)
variants: [
  { no: 1, name: "text", kind: "string" },
  { no: 2, name: "payload", kind: "message", T: () => PayloadRef },
]
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm every field number on the wire is declared before decoding:
// (schema-side check) ensure each oneof variant has a unique, expected `no`
const declared = new Set(desc.variants.map(v => v.no));
if (!declared.has(expectedIncomingFieldNo)) {
  throw new Error(`descriptor does not declare oneof field ${expectedIncomingFieldNo}; update the schema`);
}

Try / catch

let msg;
try {
  msg = MyMsg.decode(bytes);
} catch (err) {
  if (err instanceof Error && /^Unknown oneof field \d+$/.test(err.message)) {
    // schema lag: log the field number, request upgrade, or fall back to raw payload
    logger.warn("peer sent undeclared oneof field; refresh descriptors", { err: err.message });
    return null; // or re-request from sender
  }
  throw err;
}

Prevention

When it happens

Trigger: Specific conditions: (1) decoding wire bytes where the tag's field number was not registered via `variantsByNumber.set(variant.no, ...)` in the OneofFieldDesc — e.g. a variant missing from `desc.variants`; (2) sender and receiver built from different schema revisions (sender added a new oneof variant, receiver's descriptor predates it); (3) a descriptor typo where the variant's `no` doesn't match the .proto; (4) a caller hand-crafting wire bytes or dispatching a field number to the wrong compiled field handler. The call path is oneofHandler -> compileOneofField.decode(message, reader, wireType, fieldNumber) at protobuf.ts:465.

Common situations: Real-world: a service deployed a newer .proto with an added oneof arm while the client still bundles the old generated descriptor; a duplicated or shifted field number after renumbering fields in the .proto; a vendor API updated server-side and the pinned descriptor in this library is stale; hand-rolled fuzz/test bytes hitting the oneof handler.

Related errors


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