-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Description
Terraform Version
Latest from main branch (as of 2025-12-23)
Terraform Configuration Files
terraform {
backend "gcs" {
bucket = "my-terraform-state-bucket"
prefix = "live/global/backend"
}
}Expected Behavior
When migrating from local backend to GCS backend, if the user lacks sufficient permissions (e.g., storage.objects.get), Terraform should display a clear error message indicating the permission issue.
Actual Behavior
Terraform crashes with a nil pointer dereference panic.
Steps to Reproduce
- Create a GCS bucket for Terraform state
- Ensure your user has permission to create the bucket but NOT
storage.objects.getpermission (e.g., only hasstorage.buckets.create) - Configure a GCS backend in your Terraform configuration
- Run
terraform initto migrate from local state to GCS backend
Crash Output
❯ tf init -migrate-state
Initializing the backend...
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
Terraform crashed! This is always indicative of a bug within Terraform.
Please report the crash with Terraform[1] so that we can fix this.
When reporting bugs, please include your terraform version, the stack trace
shown below, and any additional information which may help replicate the issue.
[1]: https://github.com/hashicorp/terraform/issues
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
panic: runtime error: invalid memory address or nil pointer dereference
goroutine 1 [running]:
runtime/debug.Stack()
runtime/debug/stack.go:26 +0x64
github.com/hashicorp/terraform/internal/logging.PanicHandler()
github.com/hashicorp/terraform/internal/logging/panic.go:84 +0x16c
panic({0x105b0bbc0?, 0x107f97bd0?})
runtime/panic.go:783 +0x120
github.com/hashicorp/terraform/internal/command.(*Meta).backendMigrateState_s_s(0x140006fe000, 0x1400041cd90)
github.com/hashicorp/terraform/internal/command/meta_backend_migrate.go:321 +0x768
github.com/hashicorp/terraform/internal/command.(*Meta).backendMigrateState(0x140006fe000, 0x1400041cd90)
github.com/hashicorp/terraform/internal/command/meta_backend_migrate.go:138 +0x414
github.com/hashicorp/terraform/internal/command.(*Meta).backend_C_r_s(0x140006fe000, 0x140004fc500, 0x7828c893, 0x140007482a0, 0x1400067f418)
github.com/hashicorp/terraform/internal/command/meta_backend.go:1215 +0xfe4
github.com/hashicorp/terraform/internal/command.(*Meta).backendFromConfig(0x140006fe000, 0x1400049f418)
github.com/hashicorp/terraform/internal/command/meta_backend.go:780 +0xdd0
github.com/hashicorp/terraform/internal/command.(*Meta).Backend(0x140006fe000, 0x0?)
github.com/hashicorp/terraform/internal/command/meta_backend.go:119 +0x60
github.com/hashicorp/terraform/internal/command.(*InitCommand).initBackend(0x140006fe000, {0x1061f4b50?, 0x140000fedc0?}, 0x1400070f9e0, {{0x1050446bd?, 0x1028b3fbc?}, 0x1400000f428?}, 0x48, {0x1061fb760, 0x1400011c110})
github.com/hashicorp/terraform/internal/command/init.go:360 +0xd80
github.com/hashicorp/terraform/internal/command.(*InitCommand).run(0x140006fe000, 0x140006380d0, {0x1061fb760, 0x1400011c110})
github.com/hashicorp/terraform/internal/command/init_run.go:185 +0xa90
github.com/hashicorp/terraform/internal/command.(*InitCommand).Run(0x140006fe000, {0x1400011a200?, 0x105be05a0?, 0x0?})
github.com/hashicorp/terraform/internal/command/init.go:74 +0x120
github.com/hashicorp/cli.(*CLI).Run(0x140003ea280)
github.com/hashicorp/cli@v1.1.7/cli.go:265 +0x420
main.realMain()
github.com/hashicorp/terraform/main.go:339 +0x1784
main.main()
github.com/hashicorp/terraform/main.go:64 +0x1c
Root Cause
In internal/command/meta_backend_migrate.go around line 284-323, the code calls opts.Destination.StateMgr() which can return errors in the diagnostics. However, the code only handles the specific case of backend.ErrDefaultWorkspaceNotSupported:
destinationState, sDiags := opts.Destination.StateMgr(opts.destinationWorkspace)
if sDiags.HasErrors() && sDiags.Err().Error() == backend.ErrDefaultWorkspaceNotSupported.Error() {
// Special handling for workspace not supported...
}
// No handling for OTHER errors - destinationState is nil!
if err := destinationState.RefreshState(); err != nil { // <- CRASH: nil pointer dereferenceWhen any error OTHER than ErrDefaultWorkspaceNotSupported occurs (such as permission errors, network errors, etc.), the code skips the if block, leaving destinationState as nil, then immediately tries to call destinationState.RefreshState(), causing the panic.
Additional Context
This is similar to #24100 which reported the same crash for the OSS backend. The root cause is identical - the code doesn't properly handle all error cases from StateMgr().
The bug affects any backend migration scenario where StateMgr() returns an error that isn't ErrDefaultWorkspaceNotSupported, including:
- GCS backend with insufficient permissions (as shown here)
- OSS backend (as reported in Terraform crashed when change backend from s3 to oss #24100)
- Potentially other backends with similar error conditions
Fix Available
I have a fix ready that properly handles all errors from StateMgr() by adding an else clause to return the error immediately instead of allowing the code to continue with a nil destinationState. Will submit a PR shortly.