{"record":{"id":"1fc8a5e29cd6f171","repo":"apache/iceberg","slug":"invalid-wkb-expected-dimensions-s-but-found-s","errorCode":null,"errorMessage":"Invalid WKB: expected dimensions %s but found %s","messagePattern":"Invalid WKB: expected dimensions (.+?) but found (.+?)","errorType":"exception","errorClass":"InvalidWkbException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/GeometryBoundsBuilder.java","lineNumber":177,"sourceCode":"    // 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);\n    // an element of a multi geometry or collection must match its parent's member type and\n    // dimensions; if/throw so the message is built only when a value is actually rejected\n    if (expectedType != ANY_GEOMETRY && geometryType != expectedType) {\n      throw new InvalidWkbException(\n          \"Invalid WKB: expected geometry type \"\n              + typeName(expectedType)\n              + \" but found \"\n              + typeName(geometryType));\n    }\n    if (expectedDimension != ANY_DIMENSION && dimensionGroup != expectedDimension) {\n      throw new InvalidWkbException(\n          \"Invalid WKB: expected dimensions \"\n              + dimensionName(expectedDimension)\n              + \" but found \"\n              + dimensionName(dimensionGroup));\n    }\n\n    int numDimensions = numDimensions(dimensionGroup);\n\n    // checkWkb above already constrained geometryType to the seven OGC types, so no default arm\n    // is reachable here\n    switch (geometryType) {\n      case TYPE_POINT -> readCoordinate(buffer, numDimensions);\n      case TYPE_LINE_STRING -> readCoordinateSequence(buffer, numDimensions);\n      case TYPE_POLYGON -> readPolygon(buffer, numDimensions);\n      case TYPE_MULTI_POINT -> readCollection(buffer, TYPE_POINT, dimensionGroup);\n      case TYPE_MULTI_LINE_STRING -> readCollection(buffer, TYPE_LINE_STRING, dimensionGroup);\n      case TYPE_MULTI_POLYGON -> readCollection(buffer, TYPE_POLYGON, dimensionGroup);\n      case TYPE_GEOMETRY_COLLECTION -> readCollection(buffer, ANY_GEOMETRY, dimensionGroup);","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/GeometryBoundsBuilder.java#L159-L195","documentation":"ISO WKB encodes dimensions (XY, XYZ, XYM, XYZM) in the thousands digit of the type code. When building geometry bounds for a column with a known dimension, a value whose dimension group differs from the expected one is rejected with InvalidWkbException, because a Z or M variant does not match the column's declared geometry dimension. addValue catches this and suppresses the file's bounds rather than failing the write.","triggerScenarios":"Parsing a geometry whose dimension group (typeCode/1000) differs from expectedDimension, e.g. an XYZ (Z=1) or XYM (M=2) value written into a column expected to be plain XY, or a mismatch between members of a multi-geometry.","commonSituations":"Schema changed from GEOMETRY(XY) to GEOMETRY(Z) (or vice versa) without rewriting old files; a writer emits Z coordinates while the column metadata says 2D; mixed-dimensional values from upstream GIS tools.","solutions":["Make the writer emit values matching the column's declared dimension (drop Z/M or update the column type).","Rewrite inconsistent files so all values share the column's dimension.","Check the geometry type code's thousands digit of the failing value to confirm the dimension mismatch.","If bounds silently disappear, find the offending file and normalize its geometry values."],"exampleFix":"// before\n// column expects XY but value carries Z (type code 1001 = Z point)\nbuilder.addValue(pointWithZWkb);\n// after\nGeometry pt = pointWithZ.dropZ(); // re-encode as 2D WKB\nbuilder.addValue(pt.toWkb());","handlingStrategy":"validation","validationCode":"static int wkbDimensionGroup(ByteBuffer wkb) {\n  int type = wkb.getInt(wkb.position() + 1); // skip order byte\n  return type / 1000; // 0=XY 1=XYZ 2=XYM 3=XYZM\n}\n// compare against the column's declared dimension before writing","typeGuard":"boolean matchesExpectedDimension(ByteBuffer wkb, int expectedGroup) {\n  return wkb != null && wkb.remaining() >= 5 && wkbDimensionGroup(wkb) == expectedGroup;\n}","tryCatchPattern":null,"preventionTips":["Keep column geometry dimension and writer output in sync (drop Z/M explicitly when the column is XY).","When changing a column's dimension, rewrite existing files.","Normalize dimensions at ingestion for GIS sources that mix 2D/3D."],"tags":["wkb","geometry","dimension-mismatch","schema"],"backgroundTag":"type-mismatch","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}