{"record":{"id":"ffba1afac5a36d6c","repo":"pulumi/pulumi","slug":"v-is-not-allowed-by-the-plan-this-resource-is-co","errorCode":null,"errorMessage":"%v is not allowed by the plan: this resource is constrained to %v","messagePattern":"(.+?) is not allowed by the plan: this resource is constrained to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resource/deploy/step_generator.go","lineNumber":452,"sourceCode":"\t\tsteps = append(prepend, steps...)\n\t}\n\n\t// Check each proposed step against the relevant resource plan, if any\n\tfor _, s := range steps {\n\t\tlogging.V(5).Infof(\"Checking step %s for %s\", s.Op(), s.URN())\n\n\t\tif sg.deployment.plan != nil {\n\t\t\tif resourcePlan, ok := sg.deployment.plan.ResourcePlans[s.URN()]; ok {\n\t\t\t\tif len(resourcePlan.Ops) == 0 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"%v is not allowed by the plan: no more steps were expected for this resource\", s.Op())\n\t\t\t\t}\n\t\t\t\tconstraint := resourcePlan.Ops[0]\n\t\t\t\t// We remove the Op from the list before doing the constraint check.\n\t\t\t\t// This is because we look at Ops at the end to see if any expected operations didn't attempt to happen.\n\t\t\t\t// This op has been attempted, it just might fail its constraint.\n\t\t\t\tresourcePlan.Ops = resourcePlan.Ops[1:]\n\t\t\t\tif !ConstrainedTo(s.Op(), constraint) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"%v is not allowed by the plan: this resource is constrained to %v\", s.Op(), constraint)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif !ConstrainedTo(s.Op(), OpSame) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"%v is not allowed by the plan: no steps were expected for this resource\", s.Op())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we're generating plans add the operation to the plan being generated\n\t\tif sg.deployment.opts.GeneratePlan {\n\t\t\t// Resource plan might be aliased\n\t\t\turn, isAliased := sg.aliased[s.URN()]\n\t\t\tif !isAliased {\n\t\t\t\turn = s.URN()\n\t\t\t}\n\t\t\tif resourcePlan, ok := sg.deployment.newPlans.get(urn); ok {\n\t\t\t\t// If the resource is in the plan, add the operation to the plan.\n\t\t\t\tresourcePlan.Ops = append(resourcePlan.Ops, s.Op())","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/resource/deploy/step_generator.go#L434-L470","documentation":"During a plan-constrained deployment (pulumi preview with a plan file applied via --plan), the step generator checks each incoming resource operation against the operations the plan expects for that resource. When the runtime attempt (e.g. create, update, delete, replace) does not match the single operation the plan allows, the deployment is aborted with this error. It exists to guarantee that a `pulumi up --plan` executes exactly the steps recorded in the preview, preventing drift between preview and apply.","triggerScenarios":"Running `pulumi up --plan plan.json` when the program's behavior at apply time differs from what the recorded preview planned for a resource — e.g. the plan expects OpSame but the engine attempts an update/delete, or the plan expects a specific op but the resource produces a different one.","commonSituations":"Editing Pulumi program code or configuration between preview and apply while reusing the plan file; resource inputs depending on runtime values (computed outputs, dynamic providers, external side effects) that resolve differently at apply time; using a plan generated against a different stack/state.","solutions":["Re-run `pulumi preview --save-plan plan.json` to regenerate the plan against current code/config, then apply that fresh plan.","Ensure nothing (program code, config, upstream resource outputs) changed between preview and apply; apply the plan with the exact same stack, config, and state.","If the resource's operation legitimately depends on runtime data, do not use plan-based apply (drop --plan) so the engine can decide ops freely.","Check that the plan file was generated for the same project/stack and that the resource's URN is unchanged (no rename, parent, or type changes)."],"exampleFix":"// before: preview then changing config\npulumi preview --save-plan plan.json\npulumi config set featureFlag true\npulumi up --plan plan.json   # op no longer matches plan -> error\n\n// after: regenerate the plan after any change\npulumi config set featureFlag true\npulumi preview --save-plan plan.json\npulumi up --plan plan.json","handlingStrategy":"validation","validationCode":"// Before applying a plan, re-preview with the plan to verify ops still match:\npulumi preview --plan plan.json   # fails fast if the program diverges from the plan","typeGuard":null,"tryCatchPattern":"// In automation against the automation API:\ntry {\n    await stack.up({ plan: \"plan.json\" });\n} catch (err) {\n    if (String(err).includes(\"is not allowed by the plan\")) {\n        // regenerate the plan and retry\n        await stack.preview({ savePlan: \"plan.json\" });\n        await stack.up({ plan: \"plan.json\" });\n    } else { throw err; }\n}","preventionTips":["Regenerate the plan immediately before every plan-based apply; never reuse stale plans.","Freeze code and config between preview and apply (CI pipelines: save-plan and up in the same job).","Avoid runtime-nondeterministic inputs (timestamps, random values) in resources covered by plans."],"tags":["pulumi","plan-based-deployment","step-generator"],"backgroundTag":"plan-op-mismatch","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}