{"record":{"id":"b9766907e336e82d","repo":"apache/iceberg","slug":"cannot-update-statistics-of-a-s-table","errorCode":null,"errorMessage":"Cannot update statistics of a %s table","messagePattern":"Cannot update statistics of a (.+?) table","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java","lineNumber":99,"sourceCode":"  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\");\n  }\n\n  @Override\n  public ManageSnapshots manageSnapshots() {\n    throw new UnsupportedOperationException(","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java#L81-L117","documentation":"Statistics updates (write table statistics files) are mutation operations that BaseReadOnlyTable does not support. updateStatistics() always throws UnsupportedOperationException for a read-only table descriptor.","triggerScenarios":"Calling table.updateStatistics() on a read-only table instance, e.g. when a statistics-publishing job is handed a snapshot wrapper instead of the live table.","commonSituations":"Compute engines publishing statistics files after analysis jobs; automation that assumes every Table handle is writable.","solutions":["Use catalog.loadTable() to obtain a writable table handle before updating statistics.","Skip statistics publication for read-only descriptors by checking the descriptor first.","Publish statistics through a transaction on the real table."],"exampleFix":"// before\nreadOnlyTable.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();\n// after\nTable table = catalog.loadTable(tableIdentifier);\ntable.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();","handlingStrategy":"validation","validationCode":"if (!(table instanceof BaseReadOnlyTable)) {\n  table.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();\n}","typeGuard":"boolean supportsStatsUpdate(Table t) { return !(t instanceof BaseReadOnlyTable); }","tryCatchPattern":"try { table.updateStatistics()...commit(); } catch (UnsupportedOperationException e) { LOG.warn(\"Skipping stats update on read-only table\"); }","preventionTips":["Publish statistics only from jobs that own a writable table handle","Skip read-only descriptors in stats-publishing pipelines"],"tags":["unsupported-operation","read-only-table","statistics"],"backgroundTag":"unsupported-operation","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"}