{"record":{"id":"de8df939d7305cd4","repo":"apache/iceberg","slug":"can-t-modify-an-empty-struct","errorCode":null,"errorMessage":"Can't modify an empty struct","messagePattern":"Can't modify an empty struct","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/EmptyStructLike.java","lineNumber":45,"sourceCode":"  private EmptyStructLike() {}\n\n  static EmptyStructLike get() {\n    return INSTANCE;\n  }\n\n  @Override\n  public int size() {\n    return 0;\n  }\n\n  @Override\n  public <T> T get(int pos, Class<T> javaClass) {\n    throw new UnsupportedOperationException(\"Can't retrieve values from an empty struct\");\n  }\n\n  @Override\n  public <T> void set(int pos, T value) {\n    throw new UnsupportedOperationException(\"Can't modify an empty struct\");\n  }\n\n  @Override\n  public boolean equals(Object other) {\n    if (this == other) {\n      return true;\n    }\n\n    return other != null && getClass() == other.getClass();\n  }\n\n  @Override\n  public int hashCode() {\n    return 0;\n  }\n\n  @Override\n  public String toString() {","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/EmptyStructLike.java#L27-L63","documentation":"EmptyStructLike represents a zero-field struct (unpartitioned data). set() always throws because there is no field to write; mutating it indicates the caller is treating the shared empty placeholder as a writable record.","triggerScenarios":"Calling set(pos, value) on EmptyStructLike, e.g. when copying partition values into a StructLike buffer for a table with an empty partition spec, or writer code that unconditionally writes all projected positions.","commonSituations":"Custom appender/writer code that copies projected fields into a partition StructLike without checking whether the spec is unpartitioned; refactoring that replaced a real StructLike with the empty placeholder.","solutions":["Guard the write path: if partitionType().fields().isEmpty(), skip the set loop","Use a real writable StructLike (e.g. a Record or wrapper) sized to the partition type when fields exist","Never write into the shared EmptyStructLike instance; it is a read-only singleton by design"],"exampleFix":"// before\nfor (int i = 0; i < fields.size(); i++) {\n  struct.set(i, values.get(i));\n}\n// after\nif (!(struct instanceof EmptyStructLike)) {\n  for (int i = 0; i < fields.size(); i++) {\n    struct.set(i, values.get(i));\n  }\n}","handlingStrategy":"type-guard","validationCode":"if (struct instanceof EmptyStructLike) { return; }","typeGuard":"boolean isWritable(StructLike s) {\n  return !(s instanceof EmptyStructLike);\n}","tryCatchPattern":"try {\n  struct.set(pos, value);\n} catch (UnsupportedOperationException e) {\n  // ignore: unpartitioned empty struct has nothing to write\n}","preventionTips":["Skip write loops for empty partition specs","Treat EmptyStructLike as a read-only singleton","Branch on spec.isUnpartitioned() before writing partition fields"],"tags":["java","structlike","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}