{"record":{"id":"07d6ff20e779364e","repo":"apache/iceberg","slug":"invalid-wkb-byte-order","errorCode":null,"errorMessage":"Invalid WKB byte order: ","messagePattern":"Invalid WKB byte order: ","errorType":"exception","errorClass":"InvalidWkbException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/GeometryBoundsBuilder.java","lineNumber":148,"sourceCode":"  private void parseGeometry(ByteBuffer buffer, int expectedType, int expectedDimension) {\n    // a geometry header is a one-byte order flag followed by a four-byte type code:\n    //   +-------+-----------------------+\n    //   | order |       type code       |\n    //   | (1 B) |         (4 B)         |\n    //   +-------+-----------------------+\n    checkRemaining(buffer, Byte.BYTES + Integer.BYTES);\n\n    // each geometry carries its own byte order in its header. A nested geometry reads a single\n    // order byte and sets the order from it before any multi-byte read, and no WKB body carries\n    // data after its children, so the order left set here is never read across a sibling and does\n    // not need to be restored.\n    byte order = buffer.get();\n    if (order == 0) {\n      buffer.order(ByteOrder.BIG_ENDIAN);\n    } else if (order == 1) {\n      buffer.order(ByteOrder.LITTLE_ENDIAN);\n    } else {\n      throw new InvalidWkbException(\"Invalid WKB byte order: \" + order);\n    }\n\n    parseGeometryBodyAndUpdateBound(buffer, expectedType, expectedDimension);\n  }\n\n  private void parseGeometryBodyAndUpdateBound(\n      ByteBuffer buffer, int expectedType, int expectedDimension) {\n    long typeCode = Integer.toUnsignedLong(buffer.getInt());\n    int dimensionGroup = (int) (typeCode / DIMENSION_DIVISOR);\n    int geometryType = (int) (typeCode % DIMENSION_DIVISOR);\n    // only the seven OGC types in XY/XYZ/XYM/XYZM are bounded here; other valid OGC types (such as\n    // PolyhedralSurface, TIN, and Triangle) are unsupported and cost the value its bounds\n    checkWkb(\n        geometryType >= TYPE_POINT\n            && geometryType <= TYPE_GEOMETRY_COLLECTION\n            && dimensionGroup <= XYZM_GROUP,\n        \"Invalid or unsupported WKB geometry type: %s\",\n        typeCode);","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/GeometryBoundsBuilder.java#L130-L166","documentation":"Iceberg's WKB parser (GeometryBoundsBuilder, used while collecting geometry column bounds for metrics) reads a one-byte byte-order flag at the start of each WKB geometry. Only 0 (big endian) and 1 (little endian) are valid per the OGC WKB spec; any other first byte means the bytes are not valid WKB, so an InvalidWkbException is thrown. In normal metric collection this is caught by addValue, which marks the file's bounds incomplete and suppresses them rather than failing the write.","triggerScenarios":"Calling GeometryBoundsBuilder.addValue with a ByteBuffer whose first byte is neither 0x00 nor 0x01 - e.g. corrupt geometry bytes, a non-WKB encoding such as EWKB with flags in the first byte, GeoJSON text accidentally stored as bytes, or a truncated/shifted buffer.","commonSituations":"Writing geometry columns with a third-party serializer that emits EWKB (PostGIS style) or another nonstandard encoding; byte-offset bugs in custom readers that hand misaligned buffers to addValue; corrupted data files.","solutions":["Check the first byte of every WKB value you write: it must be 0x00 or 0x01 (or 0x01 little-endian per ISO variant).","If your producer emits PostGIS EWKB, convert to standard WKB before writing (ST_AsBinary instead of the raw geometry bytes).","Verify the buffer passed to addValue is positioned exactly at the geometry start; duplicate/reset the buffer if a reader consumed bytes earlier.","If the error surfaces indirectly (missing bounds), inspect the offending data file's geometry column values for corruption."],"exampleFix":"// before\nbyte[] bytes = pgGeometry.toEwkb(); // first byte may be 0 not 0/1 order flag\nbuilder.addValue(ByteBuffer.wrap(bytes));\n// after\nbyte[] bytes = pgGeometry.toWkb(); // standard OGC WKB, order flag 0x00/0x01\nbuilder.addValue(ByteBuffer.wrap(bytes));","handlingStrategy":"validation","validationCode":"static boolean isValidWkbHeader(java.nio.ByteBuffer buf) {\n  int pos = buf.position();\n  byte order = buf.get(pos);\n  return order == 0 || order == 1;\n}\n// call before addValue: if (!isValidWkbHeader(wkb)) skip or fix bytes;","typeGuard":"boolean isStandardWkb(ByteBuffer wkb) {\n  return wkb != null && wkb.remaining() >= 5 && (wkb.get(wkb.position()) == 0 || wkb.get(wkb.position()) == 1);\n}","tryCatchPattern":null,"preventionTips":["Write standard OGC WKB (order flag 0x00/0x01), not EWKB or custom encodings.","Never hand misaligned or partially consumed ByteBuffers to bounds builders; duplicate and rewind first.","Round-trip test one value through your serializer before bulk writes."],"tags":["wkb","geometry","byte-order","malformed-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}