v1.14 backport: Fix nil pointer dereference in backend state migration (#38028) (#38058)

* 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>
pull/38081/head
Radek Simko 3 months ago committed by GitHub
parent 1d06b6d1ac
commit 4aefefee4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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"

@ -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
}
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
}
// Update the name of the destination state.
opts.destinationWorkspace = name
// Update the name of the destination state.
opts.destinationWorkspace = name
destinationState, sDiags := opts.Destination.StateMgr(opts.destinationWorkspace)
if sDiags.HasErrors() {
return nil, sDiags.Err()
}
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()
// 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 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
}()
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(

Loading…
Cancel
Save