pulumi/pulumi · error
update timeout changed (expected %v)
Error message
update timeout changed (expected %v)
What it means
checkGoal compares CustomTimeouts between plan and program; this branch fires when the update timeout recorded in the plan differs from the program's current value.
Source
Thrown at pkg/resource/deploy/plan.go:625
default:
expected := "no value"
if rp.Goal.DeleteBeforeReplace != nil {
expected = strconv.FormatBool(*rp.Goal.DeleteBeforeReplace)
}
return fmt.Errorf("deleteBeforeReplace changed (expected %v)", expected)
}
// Check that the import ID is identical.
if rp.Goal.ID != programGoal.ID {
return fmt.Errorf("importID changed (expected %v)", rp.Goal.ID)
}
// Check that the timeouts are identical.
switch {
case rp.Goal.CustomTimeouts.Create != programGoal.CustomTimeouts.Create:
return fmt.Errorf("create timeout changed (expected %v)", rp.Goal.CustomTimeouts.Create)
case rp.Goal.CustomTimeouts.Update != programGoal.CustomTimeouts.Update:
return fmt.Errorf("update timeout changed (expected %v)", rp.Goal.CustomTimeouts.Update)
case rp.Goal.CustomTimeouts.Delete != programGoal.CustomTimeouts.Delete:
return fmt.Errorf("delete timeout changed (expected %v)", rp.Goal.CustomTimeouts.Delete)
case rp.Goal.CustomTimeouts.Read != programGoal.CustomTimeouts.Read:
return fmt.Errorf("read timeout changed (expected %v)", rp.Goal.CustomTimeouts.Read)
}
// Check that the ignoreChanges sets are identical.
if message, changed := rp.diffStringSets(rp.Goal.IgnoreChanges, programGoal.IgnoreChanges); changed {
return fmt.Errorf("ignoreChanges changed: %v", message)
}
// Check that the additionalSecretOutputs sets are identical.
if message, changed := rp.diffPropertyKeys(
rp.Goal.AdditionalSecretOutputs, programGoal.AdditionalSecretOutputs); changed {
return fmt.Errorf("additionalSecretOutputs changed: %v", message)
}
// Check that the dependencies match.View on GitHub (pinned to 793f7b2e16)
Solutions
- Restore the update timeout to the planned value, or regenerate the plan
- Re-save the plan if the timeout change is intentional
- Discard the saved plan and apply without a plan
- Keep timeout config synchronized with any pending plan files in CI
Example fix
// before: update timeout changed after plan saved
{ customTimeouts: { update: "10m" } }
// after: match planned goal
{ customTimeouts: { update: "20m" } } Defensive patterns
Strategy: validation
Validate before calling
if curTimeouts.Update != planTimeouts.Update {
return errors.New("update timeout drifted from saved plan")
} Try / catch
if err := up(plan); err != nil && strings.Contains(err.Error(), "update timeout changed") {
return regeneratePlan()
} Prevention
- Batch timeout increases with plan regeneration
- Don't edit CustomTimeouts while a plan is pending in CI
- Regenerate the plan whenever update behavior tuning changes
- Store plan files with the exact commit that produced them
When it happens
Trigger: Replaying a saved plan after changing `customTimeouts: { update: "20m" }` on the resource, or adding/removing the update timeout after the plan was saved.
Common situations: Extending update timeouts for long-running in-place updates (e.g. cluster upgrades) while a plan is pending; inconsistent timeout edits across environments sharing a plan.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- create timeout changed (expected %v)
- delete timeout changed (expected %v)
- provider changed (expected %v)
- parent changed (expected %v)
- protect changed (expected %v)
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/009426a9089e6d3c.
Report an issue: GitHub.