{"record":{"id":"afcf00340bf3a69c","repo":"apache/iceberg","slug":"failed-to-close-scan-scan","errorCode":null,"errorMessage":"Failed to close scan: ${scan}","messagePattern":"Failed to close scan: (.+?)","errorType":"exception","errorClass":"UncheckedIOException","httpStatus":null,"severity":"error","filePath":"spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkPartitioningAwareScan.java","lineNumber":196,"sourceCode":"\n  protected synchronized List<T> tasks() {\n    if (tasks == null) {\n      try (CloseableIterable<? extends ScanTask> taskIterable = scan.planFiles()) {\n        List<T> plannedTasks = Lists.newArrayList();\n\n        for (ScanTask task : taskIterable) {\n          ValidationException.check(\n              taskJavaClass().isInstance(task),\n              \"Unsupported task type, expected a subtype of %s: %s\",\n              taskJavaClass().getName(),\n              task.getClass().getName());\n\n          plannedTasks.add(taskJavaClass().cast(task));\n        }\n\n        this.tasks = plannedTasks;\n      } catch (IOException e) {\n        throw new UncheckedIOException(\"Failed to close scan: \" + scan, e);\n      }\n    }\n\n    return tasks;\n  }\n\n  @Override\n  protected synchronized List<ScanTaskGroup<T>> taskGroups() {\n    if (taskGroups == null) {\n      if (groupingKeyType().fields().isEmpty()) {\n        CloseableIterable<ScanTaskGroup<T>> plannedTaskGroups =\n            TableScanUtil.planTaskGroups(\n                CloseableIterable.withNoopClose(tasks()),\n                adjustSplitSize(tasks(), scan.targetSplitSize()),\n                scan.splitLookback(),\n                scan.splitOpenFileCost());\n        this.taskGroups = Lists.newArrayList(plannedTaskGroups);\n","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkPartitioningAwareScan.java#L178-L214","documentation":"SparkPartitioningAwareScan.tasks() plans scan tasks inside try-with-resources; an IOException while closing the planned task iterable is wrapped as UncheckedIOException with the scan description. This signals the scan failed to complete/close cleanly, typically an underlying FileIO failure.","triggerScenarios":"Calling tasks() during Spark planning when planFiles()/planTasks() close throws IOException — missing manifests, filesystem errors, expired snapshots.","commonSituations":"Metadata corruption, concurrent snapshot expiry, or transient cloud storage errors while a Spark query plans an Iceberg scan.","solutions":["Inspect the cause chain for the real FileIO error (missing file, auth, network) and fix it.","Validate table metadata/manifest availability; re-run the query after transient storage issues clear.","If caused by concurrent expiry, avoid expiring snapshots while queries are planning against them."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// preflight: read table metadata and list manifests before planning\ntable.refresh();\ntable.currentSnapshot().allManifests(table.io()); // throws if manifests unreadable","typeGuard":null,"tryCatchPattern":"try { scan.tasks(); } catch (UncheckedIOException e) { log.error(\"scan close failed: {} cause={}\", scan, e.getCause()); throw e; }","preventionTips":["Avoid concurrent snapshot expiry during query planning","Validate table metadata integrity after storage incidents","Retry planning on transient cloud storage failures"],"tags":["spark","scan-planning","io"],"backgroundTag":"file-read-failed","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"}