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 000000000000..70513d121a79 --- /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 b8a39ab74da6..a0062ead00e3 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(