v1.14 backport: Fix nil pointer dereference in backend state migration (#38028) (#38058)
Some checks are pending
build / Determine intended Terraform version (push) Waiting to run
build / Determine Go toolchain version (push) Waiting to run
build / Generate release metadata (push) Blocked by required conditions
build / Build for freebsd_386 (push) Blocked by required conditions
build / Build for linux_386 (push) Blocked by required conditions
build / Build for openbsd_386 (push) Blocked by required conditions
build / Build for windows_386 (push) Blocked by required conditions
build / Build for darwin_amd64 (push) Blocked by required conditions
build / Build for freebsd_amd64 (push) Blocked by required conditions
build / Build for linux_amd64 (push) Blocked by required conditions
build / Build for openbsd_amd64 (push) Blocked by required conditions
build / Build for solaris_amd64 (push) Blocked by required conditions
build / Build for windows_amd64 (push) Blocked by required conditions
build / Build for freebsd_arm (push) Blocked by required conditions
build / Build for linux_arm (push) Blocked by required conditions
build / Build for darwin_arm64 (push) Blocked by required conditions
build / Build for linux_arm64 (push) Blocked by required conditions
build / Build Docker image for linux_386 (push) Blocked by required conditions
build / Build Docker image for linux_amd64 (push) Blocked by required conditions
build / Build Docker image for linux_arm (push) Blocked by required conditions
build / Build Docker image for linux_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_386 (push) Blocked by required conditions
build / Build e2etest for windows_386 (push) Blocked by required conditions
build / Build e2etest for darwin_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_amd64 (push) Blocked by required conditions
build / Build e2etest for windows_amd64 (push) Blocked by required conditions
build / Build e2etest for linux_arm (push) Blocked by required conditions
build / Build e2etest for darwin_arm64 (push) Blocked by required conditions
build / Build e2etest for linux_arm64 (push) Blocked by required conditions
build / Run e2e test for linux_386 (push) Blocked by required conditions
build / Run e2e test for windows_386 (push) Blocked by required conditions
build / Run e2e test for darwin_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_amd64 (push) Blocked by required conditions
build / Run e2e test for windows_amd64 (push) Blocked by required conditions
build / Run e2e test for linux_arm (push) Blocked by required conditions
build / Run e2e test for linux_arm64 (push) Blocked by required conditions
build / Run terraform-exec test for linux amd64 (push) Blocked by required conditions
Quick Checks / Unit Tests (push) Waiting to run
Quick Checks / Race Tests (push) Waiting to run
Quick Checks / End-to-end Tests (push) Waiting to run
Quick Checks / Code Consistency Checks (push) Waiting to run

* fix: handle all StateMgr errors during backend migration

Fixes a nil pointer dereference panic that occurs during backend
migration when StateMgr returns an error other than ErrDefaultWorkspaceNotSupported.

The bug occurred because the code only checked for the specific
ErrDefaultWorkspaceNotSupported error. When any other error occurred
(such as permission errors like storage.objects.get access denied),
destinationState remained nil, but the code continued and attempted
to call destinationState.RefreshState(), causing a panic.

This fix adds an else clause to catch and return all other errors
from StateMgr, preventing the nil pointer dereference and providing
users with a clear error message instead of a crash.

Fixes #24100

* Add changelog entry for backend migration nil pointer fix

🤖 Generated with [Claude Code](https://claude.com/claude-code)



---------

Co-authored-by: Mark Hall <mark.hall993@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Radek Simko 2026-01-14 15:20:49 +00:00 committed by GitHub
parent 1d06b6d1ac
commit 4aefefee4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 29 deletions

View file

@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'backend: Fix nil pointer dereference crash during `terraform init` when the destination backend returns an error'
time: 2025-12-23T18:45:16.000000Z
custom:
Issue: "38027"

View file

@ -282,37 +282,43 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
var err error
destinationState, sDiags := opts.Destination.StateMgr(opts.destinationWorkspace)
if sDiags.HasErrors() && sDiags.Err().Error() == backend.ErrDefaultWorkspaceNotSupported.Error() {
// If the backend doesn't support using the default state, we ask the user
// for a new name and migrate the default state to the given named state.
destinationState, err = func() (statemgr.Full, error) {
log.Print("[TRACE] backendMigrateState: destination doesn't support a default workspace, so we must prompt for a new name")
name, err := m.promptNewWorkspaceName(opts.DestinationType)
if err != nil {
return nil, err
}
// Update the name of the destination state.
opts.destinationWorkspace = name
destinationState, sDiags := opts.Destination.StateMgr(opts.destinationWorkspace)
if sDiags.HasErrors() {
return nil, sDiags.Err()
}
// Ignore invalid workspace name as it is irrelevant in this context.
workspace, _ := m.Workspace()
// If the currently selected workspace is the default workspace, then set
// the named workspace as the new selected workspace.
if workspace == backend.DefaultStateName {
if err := m.SetWorkspace(opts.destinationWorkspace); err != nil {
return nil, fmt.Errorf("Failed to set new workspace: %s", err)
if sDiags.HasErrors() {
if sDiags.Err().Error() == backend.ErrDefaultWorkspaceNotSupported.Error() {
// If the backend doesn't support using the default state, we ask the user
// for a new name and migrate the default state to the given named state.
destinationState, err = func() (statemgr.Full, error) {
log.Print("[TRACE] backendMigrateState: destination doesn't support a default workspace, so we must prompt for a new name")
name, err := m.promptNewWorkspaceName(opts.DestinationType)
if err != nil {
return nil, err
}
}
return destinationState, nil
}()
// Update the name of the destination state.
opts.destinationWorkspace = name
destinationState, sDiags := opts.Destination.StateMgr(opts.destinationWorkspace)
if sDiags.HasErrors() {
return nil, sDiags.Err()
}
// Ignore invalid workspace name as it is irrelevant in this context.
workspace, _ := m.Workspace()
// If the currently selected workspace is the default workspace, then set
// the named workspace as the new selected workspace.
if workspace == backend.DefaultStateName {
if err := m.SetWorkspace(opts.destinationWorkspace); err != nil {
return nil, fmt.Errorf("Failed to set new workspace: %s", err)
}
}
return destinationState, nil
}()
} else {
// For any other error, return it immediately to avoid nil pointer dereference
return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.DestinationType, sDiags.Err())
}
}
if err != nil {
return fmt.Errorf(strings.TrimSpace(