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

  1. Drop `-lockfile=readonly` if you actually want `-upgrade` to update the lock file.
  2. Drop `-upgrade` if the goal is to install exactly what the lock file pins, keeping `-lockfile=readonly`.
  3. 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

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


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/bd20aa47fb617979. Report an issue: GitHub.