{"record":{"id":"44f289c3691373dc","repo":"apache/iceberg","slug":"variant-column-has-only-2-children-got-ordinal-44f289","errorCode":null,"errorMessage":"Variant column has only 2 children, got ordinal: ${ordinal}","messagePattern":"Variant column has only 2 children, got ordinal: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/data/vectorized/VariantColumnVector.java","lineNumber":74,"sourceCode":"  public int numNulls() {\n    return valueChild.numNulls();\n  }\n\n  @Override\n  public boolean isNullAt(int rowId) {\n    return valueChild.isNullAt(rowId);\n  }\n\n  // getChild is what getVariant() calls: child(0) = value, child(1) = metadata\n  @Override\n  public ColumnVector getChild(int ordinal) {\n    if (ordinal == 0) {\n      return valueChild;\n    } else if (ordinal == 1) {\n      return metadataChild;\n    }\n\n    throw new IllegalArgumentException(\n        \"Variant column has only 2 children, got ordinal: \" + ordinal);\n  }\n\n  @Override\n  public boolean getBoolean(int rowId) {\n    throw unsupported();\n  }\n\n  @Override\n  public byte getByte(int rowId) {\n    throw unsupported();\n  }\n\n  @Override\n  public short getShort(int rowId) {\n    throw unsupported();\n  }\n","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/data/vectorized/VariantColumnVector.java#L56-L92","documentation":"A Spark Variant column vector is composed of exactly two children: value (ordinal 0) and metadata (ordinal 1). getChild is called by the engine to descend into struct-like children; any ordinal outside 0..1 indicates the engine is treating the variant vector as a struct with more fields than it has, so the vector throws IllegalArgumentException.","triggerScenarios":"Calling getChild(ordinal) on VariantColumnVector with ordinal >= 2, typically from a Spark plan that misinterprets the variant column's internal layout or indexes a wrong field position.","commonSituations":"Version drift between Spark's VariantVal handling and Iceberg's variant vector implementation; custom readers/code touching the variant column vector internals with wrong ordinals.","solutions":["Verify the Spark version matches the icebberg-spark-4.1 module expectations (variant support requires recent Spark 4.x)","Don't index VariantColumnVector as a struct beyond its two children; use getVariant(rowId) style access instead","Check the physical schema wasn't altered so the variant column resolves as a struct with extra fields"],"exampleFix":"// before\nColumnVector child = variantVector.getChild(2);\n// after\nif (ordinal < variantVector.getNumChildren()) {\n  ColumnVector child = variantVector.getChild(ordinal);\n}","handlingStrategy":"type-guard","validationCode":"if (ordinal < 0 || ordinal >= 2) {\n  throw new IllegalArgumentException(\"VariantColumnVector exposes only ordinals 0 (value) and 1 (metadata)\");\n}","typeGuard":"boolean isValidVariantChildOrdinal(int ordinal) { return ordinal == 0 || ordinal == 1; }","tryCatchPattern":"try {\n  ColumnVector child = variantVector.getChild(ordinal);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Variant column has only 2 children\")) {\n    throw new IllegalStateException(\"Wrong ordinal for variant child; use 0=value, 1=metadata\", e);\n  }\n  throw e;\n}","preventionTips":["Treat VariantColumnVector as a 2-child vector only","Match Spark and Iceberg versions so variant layouts agree","Use the variant accessor APIs instead of struct-style child indexing"],"tags":["spark","variant","vectorized-read"],"backgroundTag":"value-out-of-range","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}