Skip to content

Core: Fix AvroEncoderUtil header error printing literal %02X#17125

Open
anxkhn wants to merge 1 commit into
apache:mainfrom
anxkhn:fix/avro-encoder-hex-message
Open

Core: Fix AvroEncoderUtil header error printing literal %02X#17125
anxkhn wants to merge 1 commit into
apache:mainfrom
anxkhn:fix/avro-encoder-hex-message

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 7, 2026

Copy link
Copy Markdown
## What

`AvroEncoderUtil.decode` validates the two Avro magic bytes with a Guava
`Preconditions.checkState`:

```java
Preconditions.checkState(
    header0 == MAGIC_BYTES[0] && header1 == MAGIC_BYTES[1],
    "Unrecognized header bytes: 0x%02X 0x%02X",
    header0,
    header1);

Iceberg's (relocated) Guava Preconditions formats messages with
Strings.lenientFormat, which only understands the %s placeholder - not
java.util.Formatter conversions like %02X. As a result the %02X specifiers
are emitted verbatim and the two byte arguments are appended as decimal in a
trailing bracket. A header mismatch therefore reports:

Unrecognized header bytes: 0x%02X 0x%02X [16, 32]

instead of the intended hex, which obscures the actual bytes that were read.

Fix

Build the message with String.format(Locale.ROOT, "Unrecognized header bytes: 0x%02X 0x%02X", header0, header1) and pass it as the single %s argument to
checkState. This mirrors the existing, correct handling of the identical message
in IcebergDecoder.decode
(core/src/main/java/org/apache/iceberg/data/avro/IcebergDecoder.java), and uses
Locale.ROOT per the project conventions for formatting.

With the fix, a mismatched header reports:

Unrecognized header bytes: 0x10 0x20

The thrown exception type is unchanged (checkState still throws
IllegalStateException); only the message text is corrected.

Testing

Added decodeRejectsUnrecognizedHeaderBytes to
core/src/test/java/org/apache/iceberg/avro/TestAvroEncoderUtil.java, asserting
that decoding a buffer with bad magic bytes throws IllegalStateException whose
message contains the rendered uppercase hex (and not the literal %02X). The test
fails before the fix (... was: "Unrecognized header bytes: 0x%02X 0x%02X [16, 32]") and passes after it.

  • ./gradlew :iceberg-core:spotlessCheck - passes
  • ./gradlew :iceberg-core:test --tests "org.apache.iceberg.avro.TestAvroEncoderUtil" - passes

AI Disclosure

  • Model: Claude Opus 4.8
  • Platform/Tool: opencode
  • Human Oversight: partially reviewed
  • Prompt Summary: Find and fix a wrong-diagnostic bug where a Guava Preconditions
    message used an unsupported %02X format specifier; apply the minimal fix
    mirroring the existing IcebergDecoder site and add a regression test.

Notes on the disclosure block (repo AGENTS.md mandates it for AI-authored PRs):
- `Human Oversight: partially reviewed` is honest - an independent automated
  reviewer verified the diff and re-proved the test red/green, and Anas gates the
  PR before it is opened, but no line-by-line human authoring happened. Anas can
  bump this to `fully reviewed` if he reads it end to end before opening.

---

## The diff (2 files, +12 / -3)

`core/src/main/java/org/apache/iceberg/avro/AvroEncoderUtil.java`
- add `import java.util.Locale;`
- replace the 3-arg `checkState(cond, "...0x%02X 0x%02X", header0, header1)` with
  `checkState(cond, "%s", String.format(Locale.ROOT, "Unrecognized header bytes:
  0x%02X 0x%02X", header0, header1))`

`core/src/test/java/org/apache/iceberg/avro/TestAvroEncoderUtil.java`
- add `assertThatThrownBy` + `org.junit.jupiter.api.Test` imports
- add `decodeRejectsUnrecognizedHeaderBytes()` asserting the message is
  `Unrecognized header bytes: 0x10 0x20`

AvroEncoderUtil.decode validated the magic bytes with
Preconditions.checkState(cond, "Unrecognized header bytes: 0x%02X 0x%02X",
header0, header1). Guava's Preconditions formats messages with
Strings.lenientFormat, which only understands the %s placeholder, so the
%02X specifiers were emitted verbatim and the two byte arguments were
appended as decimal in a trailing bracket. A mismatched header therefore
reported "Unrecognized header bytes: 0x%02X 0x%02X [16, 32]" instead of the
intended hex, obscuring the actual bytes.

Build the message with String.format(Locale.ROOT, ...) and pass it as the
single %s argument, matching the existing handling in IcebergDecoder.decode.
Add a regression test asserting the uppercase hex is rendered.

Generated-by: opencode
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@github-actions github-actions Bot added the core label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant