hashicorp/terraform · error
The -upgrade flag conflicts with -lockfile=readonly.
Error message
The -upgrade flag conflicts with -lockfile=readonly.
What it means
Emitted by `terraform init` argument validation (internal/command/arguments/init.go:217) when `-upgrade` and `-lockfile=readonly` are passed together. `-upgrade` instructs init to re-resolve providers and update the dependency lock file, while `-lockfile=readonly` forbids any change to it; the two intentions are contradictory, so init refuses to proceed.
Source
Thrown at internal/command/arguments/init.go:220
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"The -migrate-state and -json options are mutually-exclusive",
"Terraform cannot ask for interactive approval when -json is set. To use the -migrate-state option, disable the -json option.",
))
}
if init.MigrateState && init.Reconfigure {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid init options",
"The -migrate-state and -reconfigure options are mutually-exclusive.",
))
}
if init.Upgrade && init.Lockfile == "readonly" {
// This is appended as a Go error because this validation already existed this way
// and it's been moved earlier in the process, to the arguments package.
diags = diags.Append(fmt.Errorf("The -upgrade flag conflicts with -lockfile=readonly."))
}
args := cmdFlags.Args()
if len(args) != 0 {
// No positional arguments are expected.
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"No positional arguments are expected",
"The init command does not expect any positional arguments. Did you mean to use -chdir?",
))
}
backendFlagSet := FlagIsSet(cmdFlags, "backend")
cloudFlagSet := FlagIsSet(cmdFlags, "cloud")
if backendFlagSet && cloudFlagSet {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,View on GitHub (pinned to c9def3e214)
Solutions
- Drop `-lockfile=readonly` if you actually want `-upgrade` to update the lock file.
- Drop `-upgrade` if the goal is to install exactly what the lock file pins, keeping `-lockfile=readonly`.
- For a one-off provider refresh without changing the lock file, use `-lockfile=readonly` alone and update the lock file in a separate controlled step.
Example fix
# before (contradictory flags) terraform init -upgrade -lockfile=readonly # after (upgrade allowed to write the lock file) terraform init -upgrade
Defensive patterns
Strategy: validation
Validate before calling
// Reject the contradictory init flag combination before invoking terraform.
func validateInitFlags(upgrade bool, lockfile string) error {
if upgrade && lockfile == "readonly" {
return fmt.Errorf("-upgrade and -lockfile=readonly are mutually exclusive")
}
return nil
} Prevention
- Document that `-lockfile=readonly` disables exactly what `-upgrade` does.
- In CI templates, parameterize flags so both cannot be set in the same job.
- Review flag combinations in code review when changing init pipelines.
When it happens
Trigger: Invoking `terraform init -upgrade -lockfile=readonly`. The check `init.Upgrade && init.Lockfile == "readonly"` (init.go:217) is true, so a Go error is appended to diagnostics during `ParseInit`.
Common situations: Copy-pasted CI flags where an `-upgrade` template was combined with a read-only lockfile policy; trying to refresh providers in a pipeline that treats the lockfile as immutable; misunderstanding that `-lockfile=readonly` blocks exactly what `-upgrade` needs to do.
Related errors
- The working directory already contains files. The -from-modu
- Terraform encountered problems during initialisation, includ
- can't delete default state
- The builtin provider does not support provider upgrades sinc
- Expected a workspace name as an argument, instead got an emp
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/bd20aa47fb617979.
Report an issue: GitHub.