apache/iceberg · error · NoSuchPlanIdException
Could not find plan ID
Error message
Could not find plan ID %s
What it means
InMemoryPlanningState identifies the initial (sequence-number-0) plan tasks by parsing plan-task keys; initialScanTasksFor throws NoSuchPlanIdException when no entry matching the given planId with an initial task is found. The plan either does not exist or has no initial tasks registered under it.
Solutions
- Confirm the planId matches the one returned by plan submission
- Ensure the plan has registered initial tasks (sequence number 0) before fetching
- Re-plan the scan against the current server instance if state was reset
- Check task-key construction so initial tasks carry the expected sequence number suffix
Example fix
// before Pair<List<FileScanTask>, String> initial = state.initialScanTasksFor(guessedPlanId); // after String planId = submitPlan(request); // returns registered id Pair<List<FileScanTask>, String> initial = state.initialScanTasksFor(planId);
Defensive patterns
Strategy: try-catch
Validate before calling
if (planId == null) throw new IllegalArgumentException("planId required before fetching initial tasks"); Try / catch
try {
Pair<List<FileScanTask>, String> initial = planningState.initialScanTasksFor(planId);
} catch (NoSuchPlanIdException e) {
// plan unknown: re-submit the planning request
} Prevention
- Derive planId exclusively from the plan submission response
- Ensure initial tasks are registered under the exact planId
- Do not fetch initial tasks before submission completes
- Restart-aware flows must re-plan after state resets
When it happens
Trigger: Requesting initial scan tasks for a planId that was never planned; a plan whose task keys were never registered with the initial sequence number (INITIAL_TASK_SEQUENCE_NUMBER); spelling/pluralization differences between stored and requested IDs.
Common situations: Client fetches initial tasks before plan submission completes; restart of the in-memory test server wiped registered tasks; wrong planId passed from the client.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Cannot find plan with id
- Could not find tasks for plan task
- Namespace does not exist
- View does not exist
- Can't handle
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/b2e4e6e1ba5872ca.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/rest/InMemoryPlanningState.java:157
Set<Map.Entry<String, List<FileScanTask>>> matchingEntries =
planTaskToFileScanTasks.entrySet().stream()
.filter(
entry -> {
String key = entry.getKey();
if (!key.contains(planId)) {
return false;
}
List<String> keyParts = Splitter.on("-").splitToList(key);
if (keyParts.isEmpty()) {
return false;
}
return INITIAL_TASK_SEQUENCE_NUMBER.equals(keyParts.get(keyParts.size() - 1));
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
.entrySet();
if (matchingEntries.isEmpty()) {
throw new NoSuchPlanIdException("Could not find plan ID %s", planId);
}
Map.Entry<String, List<FileScanTask>> initialEntry = Iterables.getOnlyElement(matchingEntries);
return Pair.of(initialEntry.getValue(), initialEntry.getKey());
}
void cancelPlan(String planId) {
planTaskToNext.entrySet().removeIf(entry -> entry.getKey().contains(planId));
planTaskToFileScanTasks.entrySet().removeIf(entry -> entry.getKey().contains(planId));
// Clear the ongoing plan status in case the planID is an async one.
if (asyncPlanningStates.containsKey(planId)) {
PlanStatus existingStatus = asyncPlanningStates.get(planId);
// No need to fail cancellation if the plan could not be found
if (existingStatus == null) {
return;
}
// No need to fail cancellation if the plan already terminatedView on GitHub (pinned to 86d9c8fc54)