Repository navigation
equality_ids written as list<long>, but spec and all other implementations use list<int> #3840
Description
Activity
Thanks for reporting the issue! This is definitely a serious bug. Lets see how we can best fix forward.
I took a look at this in depth. We are in fact using
longtype forequality_idswhich contradicts the spec. The good thing is, we dont writeequality_idsyet, so only the schema is wrong. And it looks like the java implementation can tolerate this difference in schema (which is why we didnt catch it in the first place). Cpp's validation is stricter so this was caught.I think the best way to fix forward is
- change
equality_idstointso we stop producing wrong schema - allow pyiceberg to tolerate
equality_idswithlong. this will allow us to read back avro files already written - implement
rewrite_manifestso that users can correct the avro files. this will write the correct schema and allow Cpp reader to work
I considered adding this quirk to the spec's implementation note, but i think its best if we just handle this internally in pyiceberg and not burden every implementation with this caveat.
EDIT:
Here is the more-detailed proposed long-term fix:-
Write
equality_idsasintfrom now on.
intis the type required by the Iceberg specification, so this prevents PyIceberg from creating any more manifests with the wrong schema. Older PyIceberg versions can still read these new manifests. -
Always read old manifests that use
longforequality_ids.
Existing PyIceberg manifests may uselong, so PyIceberg should retain permanent read support for this old format. This compatibility should apply only todata_file.equality_ids, rather than allowinglong → intconversions everywhere and potentially hiding other schema problems. -
Offer a maintenance operation to rewrite old manifests.
Users who need strict-reader compatibility, such as with iceberg-cpp, can use it to replace the legacy manifests used by the current table state with new ones that useequality_ids: int. The operation should write new manifest files and commit a new snapshot; it must not modify existing manifests in place because Iceberg manifests are immutable. Rewriting manifests referenced by every historical snapshot is a larger metadata migration and can be considered separately.
PyIceberg does not currently write equality delete files, so
equality_idsis normally empty in the data itself. However, the incorrect type is still part of every manifest schema, which is why this affects all manifests.We should not add a flag to continue writing
long. It would keep producing invalid Iceberg metadata, while permanent read support preserves access to existing manifests.Reacted by Drew, Alex Stephen and Fokko Driesprong- change
- added 3 commits that reference this issue
on Aug 31, 2026 Thanks @haoxu0 for reporting this, and it is a serious issue indeed. I think the approach that @kevinjqliu is suggesting is the right direction. When we coerce int into long, we should check for overflows (and fail).
@kevinjqliu @Fokko Thanks. Happy to help with the fix — just let me know.
Yeah, I think that'll be good to avoid writing these in the future. I think we should still give a deprecation warning on manifests with longs for equality_ids and eventually deprecate the functionality.
I was back and forth about whether to emit deprecation warning. I think the emitting a warning might be too noisy since all existing avro manifests from pyiceberg will trigger it
btw we're iterating on a fix in #3842
- added a commit that references this issue
on Sep 5, 2026 - added a commit that references this issue
on Sep 5, 2026 last step is to implement the
rewrite_manifestprocedure, i'll take a look at #3631- added a commit that references this issue
on Sep 12, 2026
Apache Iceberg version
main (development)
Please describe the bug 🐞
equality_idsis 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
format/spec.md("Manifests" table)list<136: int>api/src/main/java/org/apache/iceberg/DataFile.javaListType.ofRequired(136, IntegerType.get())src/iceberg/manifest_entry.hiceberg::int32()pyiceberg/manifest.py:298and:393LongType()Both the v1 and v2 manifest-entry schemas are affected. Note that pyiceberg's own type hint already says
int: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:
because its
kIntcase accepts onlyAVRO_INT(Avro permitsint→longpromotion, 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
The obvious fix breaks backwards compatibility
Changing
LongType()→IntegerType()in those two places makes pyiceberg unable to read every manifest it has previously written:Two reasons compound here:
pyiceberg/schema.pyregisterspromoteforIntegerType(int → long is legal) but has noLongTyperegistration, so long → int falls through to the generic case and raises (schema.py:1660).MANIFEST_ENTRY_SCHEMASis used for both directions — the writer takesfile_schemafrom it and the reader takesrecord_schemafrom it — so changing the constant changes the read path too.Verified empirically: patching field 135's element type to
IntegerTypeand then reading a manifest written by the current release raises the above.Possible directions
A correct fix has to write
intwhile continuing to readlong. Options, roughly: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
main, both affectedStatus checks
Still present on
main. Verified againstmainon 2026-08-24:manifest.py:298and:393both still declareLongType(). Separately, no commit in the repository's history mentionsequality_idsat all, and the recent commits touchingmanifest.pyare unrelated (manifest cache configuration, min sequence number,ManifestEntry.snapshot_idsetter, lint rules).No prior report found, though this is the weaker of the two claims. Searching this repository for
equality_idsreturns 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.errorafter int→long / float→double type promotion) also concerns int/long handling, but it is about promotion of user table columns breakinginspect.*on pre-promotion data files. It does not involve the manifest-entry schema orequality_ids, so the two should not be conflated.Willingness to contribute