{"record":{"id":"d8d221babb64c7ab","repo":"apache/iceberg","slug":"queue-filling-failed-d8d221","errorCode":null,"errorMessage":"Queue filling failed","messagePattern":"Queue filling failed","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/source/AsyncSparkMicroBatchPlanner.java","lineNumber":220,"sourceCode":"              }\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  /**\n   * This needs to be non destructive on the queue as spark could call this multiple times. Each\n   * time, depending on the table state it could return something different\n   *\n   * @param startOffset the starting offset of the next microbatch","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/source/AsyncSparkMicroBatchPlanner.java#L202-L238","documentation":"This wraps any Throwable caught while the background thread fills the planning queue in AsyncSparkMicroBatchPlanner. Queue filling (manifest scanning / file listing for upcoming batches) runs asynchronously; its failure is stored and rethrown as a RuntimeException by planFiles after the background loop terminates.","triggerScenarios":"The background fill-queue task throws (e.g. file listing or manifest read error); fillQueueFailedThrowable is non-null when planFiles exits the do/while loop and is rethrown.","commonSituations":"Data file deletion racing with streaming reads causing missing-file errors; S3/HDFS throttling or outages during file listing; oversized snapshots exhausting memory during queue fill.","solutions":["Check the cause of fillQueueFailedThrowable for the underlying file-listing or manifest error","Verify data files are not expired/deleted while the streaming query is running (tune expiring snapshots / history.expire.max-snapshot-age-ms)","Ensure object store availability and retry limits","Consider disabling the async planner (fall back to sync planning) if concurrent planning races persist"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { planFiles(...) } catch (RuntimeException e) { if (e.getCause() instanceof transient IO error) { retryWithBackoff() } else throw }","preventionTips":["Ensure files referenced by active snapshots are not expired during streaming","Configure object-store retry policies","Cap streaming-max-files-per-micro-batch to bound queue-fill work"],"tags":["spark","streaming","queue"],"backgroundTag":"api-error-response","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"}