From 7029dfac862a86a509eb08a19d711308dfe607fc Mon Sep 17 00:00:00 2001 From: Mark Hall Date: Wed, 14 Jan 2026 06:41:23 -0500 Subject: [PATCH] Fix nil pointer dereference in backend state migration (#38028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: Claude --------- Co-authored-by: Claude --- .changes/v1.15/BUG FIXES-20251223-184516.yaml | 5 ++ internal/command/meta_backend_migrate.go | 56 ++++++++++--------- 2 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 .changes/v1.15/BUG FIXES-20251223-184516.yaml diff --git a/.changes/v1.15/BUG FIXES-20251223-184516.yaml b/.changes/v1.15/BUG FIXES-20251223-184516.yaml new file mode 100644 index 0000000000..70513d121a --- /dev/null +++ b/.changes/v1.15/BUG FIXES-20251223-184516.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'backend: Fix nil pointer dereference crash during `terraform init -migrate-state` when the destination backend returns a permission error' +time: 2025-12-23T18:45:16.000000Z +custom: + Issue: "38027" diff --git a/internal/command/meta_backend_migrate.go b/internal/command/meta_backend_migrate.go index b8a39ab74d..a0062ead00 100644 --- a/internal/command/meta_backend_migrate.go +++ b/internal/command/meta_backend_migrate.go @@ -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(