{"record":{"id":"533f260f2efeb239","repo":"apache/iceberg","slug":"cannot-delete-from-a-s-table","errorCode":null,"errorMessage":"Cannot delete from a %s table","messagePattern":"Cannot delete from a (.+?) table","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java","lineNumber":94,"sourceCode":"  public OverwriteFiles newOverwrite() {\n    throw new UnsupportedOperationException(\"Cannot overwrite in a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public RowDelta newRowDelta() {\n    throw new UnsupportedOperationException(\n        \"Cannot remove or replace rows in a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public ReplacePartitions newReplacePartitions() {\n    throw new UnsupportedOperationException(\n        \"Cannot replace partitions in a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public DeleteFiles newDelete() {\n    throw new UnsupportedOperationException(\"Cannot delete from a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public UpdateStatistics updateStatistics() {\n    throw new UnsupportedOperationException(\n        \"Cannot update statistics of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public UpdatePartitionStatistics updatePartitionStatistics() {\n    throw new UnsupportedOperationException(\n        \"Cannot update partition statistics of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public ExpireSnapshots expireSnapshots() {\n    throw new UnsupportedOperationException(\n        \"Cannot expire snapshots from a \" + descriptor + \" table\");","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java#L76-L112","documentation":"BaseReadOnlyTable disables all row mutation, including deletes. newDelete() unconditionally throws UnsupportedOperationException because a read-only table descriptor has no commit path to rewrite or delete data files.","triggerScenarios":"Calling table.newDelete() (DeleteFiles API) on a read-only table instance such as a snapshot wrapper or metadata-backed table.","commonSituations":"Generic maintenance code (row-level deletes, GDPR-style deletes) applied to a table handle obtained from a snapshot or metadata table; copy-on-write engines acquiring a table handle through read-only APIs.","solutions":["Load a writable table via catalog.loadTable(identifier) before invoking newDelete().","Check the table handle type/capability before issuing deletes.","Perform deletes on the real table and use snapshot references only for reads."],"exampleFix":"// before\nreadOnlyTable.newDelete().deleteFromRowFilter(expr).commit();\n// after\nTable table = catalog.loadTable(tableIdentifier);\ntable.newDelete().deleteFromRowFilter(expr).commit();","handlingStrategy":"validation","validationCode":"if (!(table instanceof BaseReadOnlyTable)) {\n  table.newDelete().deleteFromRowFilter(expr).commit();\n}","typeGuard":"boolean isWritable(Table t) { return !(t instanceof BaseReadOnlyTable); }","tryCatchPattern":"try { table.newDelete()...commit(); } catch (UnsupportedOperationException e) { throw new IllegalStateException(\"Cannot delete via read-only handle\", e); }","preventionTips":["Feature-check the table handle before generic delete routines","Keep maintenance code bound to catalog-loaded tables","Document in shared libraries that snapshot handles are read-only"],"tags":["unsupported-operation","read-only-table","delete"],"backgroundTag":"unsupported-operation","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"}