{"record":{"id":"4d2a223f58d1ab7a","repo":"apache/iceberg","slug":"table-refresh-failed-4d2a22","errorCode":null,"errorMessage":"Table refresh failed","messagePattern":"Table refresh failed","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/source/AsyncSparkMicroBatchPlanner.java","lineNumber":216,"sourceCode":"              Pair<StreamingOffset, FileScanTask> nextElem = queue.peekFirst();\n              boolean endOffsetPeek = false;\n              if (nextElem != null) {\n                endOffsetPeek = endOffset.equals(nextElem.first());\n              }\n              // end offset may be synthetic and not exist in the queue\n              boolean endOffsetSynthetic =\n                  currentOffset.snapshotId() == endOffset.snapshotId()\n                      && (currentOffset.position() + 1) == endOffset.position();\n              shouldTerminate = endOffsetPeek || endOffsetSynthetic;\n            } else {\n              LOG.trace(\"planFiles hasn't reached {}, waiting\", endOffset);\n            }\n          } while (!shouldTerminate\n              && refreshFailedThrowable == null\n              && fillQueueFailedThrowable == null);\n\n          if (refreshFailedThrowable != null) {\n            throw new RuntimeException(\"Table refresh failed\", refreshFailedThrowable);\n          }\n\n          if (fillQueueFailedThrowable != null) {\n            throw new RuntimeException(\"Queue filling failed\", fillQueueFailedThrowable);\n          }\n\n          LOG.info(\n              \"completed planFiles for {}, startOffset: {}, endOffset: {}, files: {}, rows: {}\",\n              table().name(),\n              startOffset,\n              endOffset,\n              filesInPlan,\n              rowsInPlan);\n          return result;\n        });\n  }\n\n  /**","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/source/AsyncSparkMicroBatchPlanner.java#L198-L234","documentation":"The async planner refreshes the Iceberg table on a background thread; failures there are captured in refreshFailedThrowable rather than thrown immediately. When the main planFiles loop exits, it rethrows the captured cause wrapped in a RuntimeException('Table refresh failed'). This surfaces table refresh problems (e.g. metadata read errors) at the point Spark expects batch planning to complete.","triggerScenarios":"The background refresh thread fails (FileIO errors, corrupted/expired metadata, permission failures reading table metadata, REST catalog HTTP errors) while planFiles is running; the stored throwable is then rethrown when the planning loop terminates.","commonSituations":"Metadata JSON expired/deleted by concurrent expiration or retention misconfiguration; lost access to object storage credentials mid-stream; catalog outage (REST/HC) during streaming; concurrent table replacement (drop/recreate) invalidating the loaded table.","solutions":["Inspect the wrapped cause (getCause) for the real failure — storage, catalog, or metadata error.","Verify underlying storage/catalog connectivity and credentials from the Spark environment.","Check that snapshot expiration hasn't removed metadata files the stream still needs; raise retention or checkpoint more often.","Restart the streaming query from checkpoint after fixing the root cause."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    query.processAllAvailable();\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().equals(\"Table refresh failed\")) {\n        Throwable cause = e.getCause(); // storage/catalog error — alert and restart from checkpoint\n    }\n}","preventionTips":["Monitor catalog/object-store health; refresh failures usually mirror infra outages","Keep snapshot/metadata retention longer than the maximum expected stream downtime","Rotate credentials without revoking old ones mid-stream","Restart from checkpoint after fixing root cause; never delete checkpoints to 'fix' it"],"tags":["spark","streaming","table-refresh","storage"],"backgroundTag":"upstream-api-error","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"}