hashicorp/terraform · error
The -upgrade flag conflicts with -lockfile=readonly.
Error message
The -upgrade flag conflicts with -lockfile=readonly.
What it means
Thrown by terraform init when both -upgrade and -lockfile=readonly are specified simultaneously. These flags are mutually exclusive because -upgrade fetches newer provider versions (which requires modifying the dependency lock file) while -lockfile=readonly forbids any lock file changes. The check runs in the arguments package before any init work begins.
Solutions
- Remove -lockfile=readonly if you need to upgrade providers
- Remove -upgrade if you need a read-only lock file
- Split into two pipeline stages: first run init -upgrade to update providers and lock file, then subsequent runs can use -lockfile=readonly
Example fix
# before terraform init -upgrade -lockfile=readonly # after (choose one depending on intent) terraform init -upgrade # or terraform init -lockfile=readonly
Defensive patterns
Strategy: validation
Validate before calling
// Validate init flags before invoking terraform init
func validateInitFlags(upgrade bool, lockfile string) error {
if upgrade && lockfile == "readonly" {
return errors.New("-upgrade conflicts with -lockfile=readonly: " +
"upgrading providers requires writing to the lock file")
}
return nil
} Prevention
- Never combine -upgrade with -lockfile=readonly in the same init command
- In CI pipelines, separate upgrade runs (which write the lock file) from verification runs (which use readonly)
- Document which init command variant each pipeline stage uses
- Add a pre-flight flag check in wrapper scripts
When it happens
Trigger: Running terraform init -upgrade -lockfile=readonly. The arguments package detects init.Upgrade==true and init.Lockfile=="readonly" and appends this error to diagnostics.
Common situations: Copy-pasting a CI pipeline command that includes both flags; misunderstanding that upgrading providers inherently requires writing to the lock file; attempting to enforce lock file immutability while also wanting provider upgrades.
Related errors
- Error checking configuration
- Error validating destination directory
- hash string must start with a scheme keyword followed by a…
- architecture portion must not contain whitespace
- at most 1 action can be invoked per operation
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/bd20aa47fb617979.
Report an issue: GitHub.
Appendix: 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 d32a084675)