HaoXuAI opened a new issue, #3840:
URL: https://github.com/apache/iceberg-python/issues/3840

   ### Apache Iceberg version
   
   main (development)
   
   ### Please describe the bug 🐞
   
   `equality_ids` is declared as a list of **long** in the manifest-entry Avro 
schema, but the Iceberg spec, the Java reference implementation, and 
iceberg-cpp all specify **int**. pyiceberg is the only implementation that 
disagrees, so manifests it writes are rejected outright by spec-conformant 
readers.
   
   ## Evidence
   
   | source | element type of field 136 |
   |---|---|
   | Iceberg spec, `format/spec.md` ("Manifests" table) | `list<136: int>` |
   | Java, `api/src/main/java/org/apache/iceberg/DataFile.java` | 
`ListType.ofRequired(136, IntegerType.get())` |
   | iceberg-cpp, `src/iceberg/manifest_entry.h` | `iceberg::int32()` |
   | **pyiceberg, `pyiceberg/manifest.py:298` and `:393`** | **`LongType()`** |
   
   Both the v1 and v2 manifest-entry schemas are affected. Note that 
pyiceberg's own type hint already says `int`:
   
   ```python
   # pyiceberg/manifest.py:528
   def equality_ids(self) -> list[int] | None:
   ```
   
   So only the Avro schema disagrees; the Python side already treats these as 
ints.
   
   ## Impact
   
   A spec-conformant reader cannot read a pyiceberg-written manifest at all. 
iceberg-cpp fails with:
   
   ```
   Cannot read Iceberg type: int from Avro type: "long"
   ```
   
   because its `kInt` case accepts only `AVRO_INT` (Avro permits `int` → `long` 
promotion, not the reverse). Any Java or C++ consumer of pyiceberg-written 
metadata hits the same wall.
   
   This is not limited to equality-delete tables: the schema is written into 
every manifest, so an ordinary append-only table with no delete files is 
equally unreadable.
   
   ## Reproducer
   
   ```python
   import fastavro
   
   # any table written by pyiceberg
   with open("<some>-m0.avro", "rb") as f:
       schema = fastavro.reader(f).writer_schema
   
   # -> equality_ids: ["null", {"element-id": 136, "type": "array", "items": 
"long"}]
   #    spec says items should be "int"
   ```
   
   ## The obvious fix breaks backwards compatibility
   
   Changing `LongType()` → `IntegerType()` in those two places makes pyiceberg 
unable to read **every manifest it has previously written**:
   
   ```
   ResolveError: Cannot promote long to int
   ```
   
   Two reasons compound here:
   
   1. `pyiceberg/schema.py` registers `promote` for `IntegerType` (int → long 
is legal) but has no `LongType` registration, so long → int falls through to 
the generic case and raises (`schema.py:1660`).
   2. `MANIFEST_ENTRY_SCHEMAS` is used for **both** directions — the writer 
takes `file_schema` from it and the reader takes `record_schema` from it — so 
changing the constant changes the read path too.
   
   Verified empirically: patching field 135's element type to `IntegerType` and 
then reading a manifest written by the current release raises the above.
   
   ## Possible directions
   
   A correct fix has to write `int` while continuing to read `long`. Options, 
roughly:
   
   - **Split the write and read schemas** so the writer emits the spec type 
while the reader keeps accepting the historical one. Probably the cleanest, and 
it changes no promotion rules.
   - **Allow long → int narrowing** for this field specifically. Smaller diff, 
but it widens the resolver in a direction the Avro and Iceberg promotion rules 
deliberately exclude.
   
   Deliberately not proposing a patch — which trade-off is acceptable seems 
like a maintainer call, and there may be a preferred pattern for 
schema-compatibility shims that I am not aware of.
   
   ## Environment
   
   - pyiceberg 0.11.1 (current release) and `main`, both affected
   - Cross-checked against the Iceberg spec, the Java reference implementation, 
and iceberg-cpp
   - Found while making iceberg-cpp read a pyiceberg-written table
   
   
   
   ## Status checks
   
   **Still present on `main`.** Verified against `main` on 2026-08-24: 
`manifest.py:298` and `:393` both still declare `LongType()`. Separately, no 
commit in the repository's history mentions `equality_ids` at all, and the 
recent commits touching `manifest.py` are unrelated (manifest cache 
configuration, min sequence number, `ManifestEntry.snapshot_id` setter, lint 
rules).
   
   **No prior report found**, though this is the weaker of the two claims. 
Searching this repository for `equality_ids` returns 13 issues and PRs, all of 
which mention it incidentally (equality-delete tests, manifest entry status, 
the files metadata table) rather than discussing its declared type. Searches 
for `"Cannot promote long to int"` and `"Cannot read Iceberg type"` return 
nothing. It is entirely possible this has been raised somewhere I did not look 
— the dev mailing list or Slack, or phrased in a way my queries missed.
   
   **Related but distinct:** #3744 (*Metadata inspection APIs fail with 
`struct.error` after int→long / float→double type promotion*) also concerns 
int/long handling, but it is about promotion of **user table columns** breaking 
`inspect.*` on pre-promotion data files. It does not involve the manifest-entry 
schema or `equality_ids`, so the two should not be conflated.
   
   ### Willingness to contribute
   
   - [ ] I can contribute a fix for this bug independently
   - [x] I would be willing to contribute a fix for this bug with guidance from 
the Iceberg community
   - [ ] I cannot contribute a fix for this bug at this time
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to