{"record":{"id":"a2e2063b25587613","repo":"apache/iceberg","slug":"encountered-invalid-summary-for-snapshot-the-f","errorCode":null,"errorMessage":"Encountered invalid summary for snapshot {}: the field 'operation' is required but missing, setting 'operation' to overwrite","messagePattern":"Encountered invalid summary for snapshot (.+?): the field 'operation' is required but missing, setting 'operation' to overwrite","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/src/main/java/org/apache/iceberg/SnapshotParser.java","lineNumber":164,"sourceCode":"          sNode);\n\n      if (sNode.size() > 0) {\n        ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();\n        Iterator<String> fields = sNode.fieldNames();\n        while (fields.hasNext()) {\n          String field = fields.next();\n          if (field.equals(OPERATION)) {\n            operation = JsonUtil.getString(OPERATION, sNode);\n          } else {\n            builder.put(field, JsonUtil.getString(field, sNode));\n          }\n        }\n        summary = builder.build();\n\n        // When the operation is not found, default to overwrite\n        // to ensure that we can read the summary without raising an exception\n        if (operation == null) {\n          LOG.warn(\n              \"Encountered invalid summary for snapshot {}: the field 'operation' is required but missing, setting 'operation' to overwrite\",\n              snapshotId);\n          operation = DataOperations.OVERWRITE;\n        }\n      }\n    }\n\n    Integer schemaId = JsonUtil.getIntOrNull(SCHEMA_ID, node);\n\n    Long firstRowId = JsonUtil.getLongOrNull(FIRST_ROW_ID, node);\n    Long addedRows = JsonUtil.getLongOrNull(ADDED_ROWS, node);\n\n    String keyId = JsonUtil.getStringOrNull(KEY_ID, node);\n\n    if (node.has(MANIFEST_LIST)) {\n      // the manifest list is stored in a manifest list file\n      String manifestList = JsonUtil.getString(MANIFEST_LIST, node);\n      return new BaseSnapshot(","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/SnapshotParser.java#L146-L182","documentation":"SnapshotParser.fromJson requires the 'operation' field in a snapshot summary. To stay forward/backward compatible with metadata written by buggy or very old writers, a missing operation is not rejected: a warning is logged and the operation defaults to 'overwrite' so summary parsing succeeds. The snapshot loads normally, but the operation value is a synthesized default.","triggerScenarios":"Reading table metadata containing a snapshot whose summary lacks the 'operation' key — e.g. metadata written by a broken custom writer, manually edited metadata JSON, or corrupt/partial summary objects from an old Iceberg version.","commonSituations":"Custom engines writing snapshots without building summaries correctly, metadata surgery performed by external scripts, and reading legacy tables migrated from very old Iceberg builds.","solutions":["No action required to read: the snapshot loads with operation='overwrite'.","Fix the writer that produced snapshots without an operation; every new snapshot must set DataOperations in its summary.","Write new commits (e.g. a trivial rewrite) so a valid snapshot becomes current, or repair the metadata JSON to add 'operation'.","If the wrong default matters for your logic, validate table.snapshots() summaries after loading migrated tables."],"exampleFix":"// before: broken summary written by a custom writer\nImmutableMap.of(\"added-data-files\", \"3\")\n// after: always include the operation\nImmutableMap.of(\n    \"operation\", DataOperations.APPEND,\n    \"added-data-files\", \"3\")","handlingStrategy":"validation","validationCode":"for (Snapshot s : table.snapshots()) {\n  Preconditions.checkState(s.summary() != null && s.summary().get(\"operation\") != null,\n      \"Snapshot \" + s.snapshotId() + \" summary missing operation\");\n}","typeGuard":"boolean hasOperation(Snapshot s) {\n  return s.summary() != null && s.summary().get(\"operation\") != null;\n}","tryCatchPattern":"// library never throws here; if the default matters:\nSnapshot s = table.currentSnapshot();\nif (!hasOperation(s)) { /* treat as overwrite or reject the table */ }","preventionTips":["Always set DataOperations in snapshot summaries when writing custom commits","Avoid manual metadata JSON editing","Validate summaries of snapshots produced by custom engines in CI"],"tags":["snapshot","parsing","metadata","forward-compatibility"],"backgroundTag":"missing-required-argument","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"}