pulumi/pulumi · error
no plan for resource %v
Error message
no plan for resource %v
What it means
While computing an update's goal plan for a resource that is being recreated (replaced) or imported, the engine expects a pre-created entry in newPlans (created during the delete/recreate or import step). If none exists, the resource's output plan cannot be filled in (Seed and Goal) and the deployment fails. This is an internal invariant: recreation and import must have registered the plan earlier in the same deployment.
Source
Thrown at pkg/resource/deploy/step_generator.go:1389
new.Inputs = inputs
}
if isTargeted {
sg.queueUntargetedDependencySames(new)
}
// If the resource is valid and we're generating plans then generate a plan
if !invalid && sg.deployment.opts.GeneratePlan {
if recreating || wasExternal || sg.isTargetedReplace(urn, old) || old == nil {
oldInputs = nil
}
inputDiff := oldInputs.Diff(inputs)
// Generate the output goal plan, if we're recreating or imported this it should already exist
if recreating || imported {
plan, ok := sg.deployment.newPlans.get(urn)
if !ok {
return nil, false, fmt.Errorf("no plan for resource %v", urn)
}
// The plan will have had it's Ops already partially filled in for the delete operation, but we
// now have the information needed to fill in Seed and Goal.
plan.Seed = randomSeed
plan.Goal = NewGoalPlan(inputDiff, goal)
} else {
newResourcePlan := &ResourcePlan{
Seed: randomSeed,
Goal: NewGoalPlan(inputDiff, goal),
}
sg.deployment.newPlans.set(urn, newResourcePlan)
}
}
// If there is a plan for this resource, validate that the program goal conforms to the plan.
// If theres no plan for this resource check that nothing has been changed.
// We don't check plans if the resource is invalid, it's going to fail anyway.
if !invalid && sg.deployment.plan != nil {View on GitHub (pinned to 793f7b2e16)
Solutions
- Upgrade the Pulumi CLI — plan generation edge cases (import/recreate) are commonly fixed in newer releases.
- Avoid combining --save-plan with the triggering operation (e.g. do the import or targeted replace in a normal up, then generate a plan afterwards).
- Check that aliases/URNs are unchanged so the recreate path finds the plan entry under the same URN.
- File a reproducible issue with pulumi/pulumi if a simple replace triggers it on the latest CLI.
Example fix
// before: importing while generating a plan pulumi import aws.s3.Bucket b bucket-id --save-plan plan.json # fails // after: separate the import from plan generation pulumi import aws.s3.Bucket b bucket-id pulumi preview --save-plan plan.json
Defensive patterns
Strategy: try-catch
Try / catch
// Automation API: fall back when plan generation hits recreate/import edge cases
try {
await stack.preview({ savePlan: "plan.json" });
} catch (err) {
if (String(err).includes("no plan for resource")) {
console.warn("Plan generation unsupported for this change; running normal preview.");
await stack.preview();
} else { throw err; }
} Prevention
- Keep the Pulumi CLI up to date when using --save-plan with replaces or imports.
- Separate imports and targeted replaces from plan-generation deployments.
- Preserve resource aliases/URNs so the recreate path finds existing plan entries.
When it happens
Trigger: During `pulumi preview --save-plan` (GeneratePlan), a resource marked as recreating or imported reaches the goal-plan stage but sg.deployment.newPlans has no entry for its URN — e.g. the delete side of a replacement didn't create the plan, or the import path skipped plan registration.
Common situations: Replacing resources with aliases where the recreate URN lookup misses; `pulumi import` combined with --save-plan; engine bugs in plan generation for edge cases (targeted replace, provider replacement).
Related errors
- Expected a new resource plan for %v
- %v is not allowed by the plan: this resource is constrained
- %v is not allowed by the plan: no steps were expected for th
- could not parse provider reference %s for %s: %w
- normalizing resource goal for %s after state migration: %w
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/ba3a7272df5308f5.
Report an issue: GitHub.