apache/iceberg · error · NoSuchPlanIdException
Cannot find plan with id
Error message
Cannot find plan with id %s
What it means
InMemoryPlanningState (used by the in-memory REST test server) tracks asynchronously submitted scan plans by ID. asyncPlanStatus looks up the plan's status; if the ID is not in the map, NoSuchPlanIdException is thrown. This means the caller asked about a plan that was never submitted or has been forgotten by this state store.
Solutions
- Submit the plan first (via the async plan endpoint) and use the returned plan ID
- Verify the plan ID string matches the one returned by plan submission exactly
- Re-submit the plan if the in-memory state server was restarted
- Check client/server flow ordering: status polls must follow a successful submit
Example fix
// before
String planId = someStaleId; // may not exist
// after
String planId = restClient.post("v1/plan", planReq, PlanResponse.class).planId();
PlanStatus status = planningState.asyncPlanStatus(planId); Defensive patterns
Strategy: try-catch
Validate before calling
if (planId == null || planningState == null) throw new IllegalStateException("Plan must be submitted before polling status"); Type guard
boolean planExists(String planId, InMemoryPlanningState s) {
return planId != null && s != null && s.asyncPlanStatusOrNull(planId) != null; // if such accessor exists
} Try / catch
try {
status = planningState.asyncPlanStatus(planId);
} catch (NoSuchPlanIdException e) {
status = PlanStatus.FAILED; // or re-submit the plan
} Prevention
- Keep the plan ID returned by plan submission and use only that
- Do not persist plan IDs across in-memory server restarts
- Poll status only after a confirmed successful submit
- Log the submitted plan ID for correlation
When it happens
Trigger: Polling plan status via the REST planning endpoint with an unknown/typo'd plan ID; querying a plan before submitting it; using a plan ID from a different server instance or after the in-memory state was recreated.
Common situations: Client retried a request against a restarted in-memory test server (IDs not persisted); race where a status poll fires before the SUBMITTED registration; stale plan IDs held across test setups.
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
- Could not find plan 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/fd061d345343b47b.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/rest/InMemoryPlanningState.java:84
void addPlanTask(String planTaskKey, List<FileScanTask> tasks) {
planTaskToFileScanTasks.put(planTaskKey, tasks);
}
void addNextPlanTask(String currentTask, String nextTask) {
planTaskToNext.put(currentTask, nextTask);
}
void addAsyncPlan(String plan) {
PlanStatus existingStatus = asyncPlanningStates.get(plan);
Preconditions.checkArgument(
existingStatus == null, "Plan %s already exists with status %s", plan, existingStatus);
asyncPlanningStates.put(plan, PlanStatus.SUBMITTED);
}
PlanStatus asyncPlanStatus(String plan) {
PlanStatus existingStatus = asyncPlanningStates.get(plan);
if (existingStatus == null) {
throw new NoSuchPlanIdException("Cannot find plan with id %s", plan);
}
return asyncPlanningStates.get(plan);
}
void markAsyncPlanAsComplete(String plan) {
PlanStatus existingStatus = asyncPlanningStates.get(plan);
Preconditions.checkArgument(existingStatus != null, "Cannot find plan %s", plan);
Preconditions.checkArgument(
existingStatus == PlanStatus.SUBMITTED,
"Cannot mark plan %s as completed as it is %s",
plan,
existingStatus);
asyncPlanningStates.put(plan, PlanStatus.COMPLETED);
}
void markAsyncPlanFailed(String plan) {
PlanStatus existingStatus = asyncPlanningStates.get(plan);View on GitHub (pinned to 86d9c8fc54)