Skip to content

codegen: a descriptor set that DescriptorPool rejects generates code whose descriptor_pool() panics at first use #626

Open

Description

@iainmcgin

Codegen and DescriptorPool validate a FileDescriptorSet independently, and the pool enforces rules that codegen does not: a scalar field that also has a type_name (PoolError::UnexpectedTypeName), duplicate field numbers and names, invalid oneof indexes, malformed map entries, reserved-range and extension-range rules, and the rest of the PoolError enum (buffa-descriptor/src/pool.rs). protoc output satisfies all of them. A set passed to Config::descriptor_set or descriptor_set_bytes that was built by hand or by another tool may not.

With generate_reflection on, codegen embeds the set and emits

DescriptorPool::decode_with_options(FILE_DESCRIPTOR_SET_BYTES, &options)
    .expect("buffa-codegen emitted a linkable FileDescriptorSet")

in each form of descriptor_pool() (buffa-codegen/src/reflect.rs). For a set the pool rejects, the build succeeds and the first reflective call in the consumer's process panics. The generated doc says a panic "indicates a codegen bug, not consumer input", which is wrong for these inputs.

The failure belongs at generation time. When reflection is on, generate_with_diagnostics can build a DescriptorPool from the same descriptors and return its error as a CodeGenError; the alternative is one validation pass shared by the two crates. buffa-codegen depends on buffa-descriptor without its reflect feature today, so the first option turns that feature on for the codegen toolchain.

Activity

  1. harshitwandhare commented on Oct 28, 2026

    @harshitwandhare
    Contributor

    I'd like to take this one. Reproduced on main at 1047169:

    • A hand-built set with one message p626.M whose int32 x = 1 also carries type_name: ".p626.M". generate with generate_reflection = true returns Ok (3 files, the descriptor_pool() with the expect is in the output).
    • DescriptorPool::decode on the bytes encode_fds_once produces for that set returns Err(UnexpectedTypeName { field: "p626.M.x", type_name: ".p626.M" }), so the generated descriptor_pool() panics on first call.

    Plan, going with your first option:

    • In generate_with_diagnostics, when generate_reflection is on, link the same override-applied file_descriptors that get embedded with DescriptorPool::new and map a PoolError to a new CodeGenError variant that carries it, before any output is produced. Shared-pool mode embeds nothing in generate, so I'd run the same check there too, since the front-end embeds the same set.
    • Turn on buffa-descriptor's reflect feature in buffa-codegen. It is reflect = [], so it adds no crates, only compiles the pool module into the toolchain.
    • Fix the generated doc: the panic can still mean a codegen bug, but no longer "not consumer input" once this lands, so it would say the set was linked at generation time.
    • Tests: the UnexpectedTypeName case and a second PoolError that I've confirmed codegen lets through today, both failing on main, plus a check that reflection off still generates as before.

    One question before I build it: should the link only run with reflection on, as the issue says, or always? With reflection off nothing embeds the set, so I'd keep it gated unless you want codegen to reject these sets regardless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions