COOLJAPAN
← All posts

OxiProto 0.1.5 Released — Protobuf Editions Actually Work Now (prost-reflect Used to Panic on the Word)

OxiProto 0.1.5 ships Editions 2023 support end to end: full feature resolution (field_presence, enum_type, repeated_field_encoding, utf8_validation, message_encoding, json_format), enforcement at decode time, and a downlevel-to-proto2 rewrite so the prost-reflect facade — which panics trying to even format the 'unknown syntax editions' error — can handle Editions files at all. Plus a wrong-buffer bug in generated packed-repeated decode, a proto2 packing default that diverged from protoc, and two new nesting-depth DoS bounds. Pure Rust Protocol Buffers, no protoc required.

release oxiproto pure-rust cooljapan noffi protobuf editions serialization codegen

prost-reflect 0.16.5 doesn’t just reject a Protobuf Editions file — it panics while formatting the error message that was supposed to explain why.

Today we released OxiProto 0.1.5 — the COOLJAPAN Pure-Rust Protocol Buffers stack. This release brings Protobuf Editions (edition = "2023";) support the rest of the way: full spec-compliant feature resolution, decode-time enforcement, and — because the facade library everyone’s reflection path runs through can’t handle Editions files at all — a rewrite step that downlevels an Editions file into the proto2 equivalent it can.

No C++. No protoc binary on PATH. OxiProto’s native .proto parser and codegen stay Pure Rust; this release makes them correct for a syntax that Protobuf’s own reference tooling shipped over a year ago and most of the Rust ecosystem still can’t read.

Why OxiProto 0.1.5 is a game changer

Editions replaced proto2/proto3’s fixed syntax choice with six independently resolvable features — but getting from a parsed .proto file to something usable turned out to require solving three separate problems:

OxiProto 0.1.5 solves all three:

Technical Deep Dive: three bugs the Editions work surfaced elsewhere

  1. Generated code packed proto2 repeated scalars, diverging from protoc on the wire. oxiproto-codegen defaulted every unpacked-by-omission repeated scalar to packed, but protoc emits the expanded form for proto2. Fine for decoding (both forms are accepted either way) but wrong for anything doing byte-comparison, signing, or deterministic serialization against a real protoc-generated peer. The generator now classifies the file’s syntax and applies the correct per-syntax default.
  2. Generated packed-repeated decode read from the wrong buffer. The packed element loop tested the nested packed buffer for emptiness but read from the enclosing message’s buffer — silently consuming the next fields on the wire or throwing UnexpectedEof. The identical bug hit map-entry key/value decoding. Found by the new proto2 packing tests, now fixed with the buffer name threaded explicitly.
  3. Nested types referenced an undefined name in flat codegen layout. A field referring to .pkg.Outer.Inner generated code referencing the bare Inner instead of the flattened Outer_Inner codegen actually emits — code that didn’t compile whenever a schema had a nested message or enum field. This is unavoidable to trip for group fields, which always synthesize a nested message.

Both new nesting-depth DoS bounds — a 100-level cap on the text-format parser/encoder and a matching cap on the .proto source parser — close the same unbounded-recursion class the 0.1.4 wire-decode fixes addressed, now covering the two paths that were still open: DynamicMessage::from_text/to_text, and every CLI subcommand that reads a user-supplied .proto file.

Getting Started

[dependencies]
oxiproto = "0.1.6"

[build-dependencies]
oxiproto-build = "0.1.6"
fn main() {
    oxiproto_build::compile_protos(&["proto/my_service.proto"], &["proto/"]).unwrap();
}

Or reflect over an Editions file directly — the downlevel rewrite happens automatically:

use oxiproto_reflect::pool_from_fds_bytes;

// Works even though the source .proto declares `edition = "2023";` --
// pool_from_fds_bytes downlevels it to proto2 internally before handing
// it to the prost-reflect facade, which cannot parse "editions" at all.
let pool = pool_from_fds_bytes(&file_descriptor_set_bytes)?;

What’s New in 0.1.5

Tips

This is the foundation

A protobuf stack that gets Editions right — resolution, enforcement, and interop with the reflection facade everyone actually uses — matters for anything doing schema evolution across proto2, proto3, and Editions in the same fleet. OxiRPC pins oxiproto/oxiproto-core/oxiproto-build/oxiproto-reflect for its RPC wire layer, and OxiRS pins oxiproto-build for protobuf codegen without a protoc dependency.

Repository: https://github.com/cool-japan/oxiproto

Star the repo if “the reflection library panics on the word ‘editions’” is exactly the kind of gap you’d rather have closed before you hit it in production.

The era of Protobuf Editions being a syntax you could declare but not actually use is over. Pure Rust Protocol Buffers that are fast, safe, and sovereign — is here.

KitaSan at COOLJAPAN OÜ August 6, 2026

↑ Back to all posts