Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
260f9d8
Replace `errBackendLocalRead` constant with custom error of same name
SarahFrench Oct 10, 2025
4b9419f
Replace `errBackendNoExistingWorkspaces` constant with custom error o…
SarahFrench Oct 10, 2025
460f41c
Replace `errBackendMigrateLocalDelete` constant with custom error of …
SarahFrench Oct 10, 2025
c096017
Replace `errBackendNewUnknown` constant with custom error of same name
SarahFrench Oct 10, 2025
efefbb1
Replace `errBackendSavedUnknown` constant with custom error of same name
SarahFrench Oct 10, 2025
0ff9e1a
Replace `errBackendClearSaved` constant with custom error of same name
SarahFrench Oct 10, 2025
40d1c94
Replace `errBackendInit` constant with custom error of same name
SarahFrench Oct 10, 2025
91dba8d
Replace `errBackendInitCloud` constant with custom error of same name
SarahFrench Oct 10, 2025
349320c
Replace `errBackendWriteSaved` constant with custom error of same name
SarahFrench Oct 10, 2025
9ff0165
Move `migrateOrReconfigDiag` next to custom errors
SarahFrench Oct 10, 2025
e469981
Replace `errBackendInit` custom error with `func errBackendInitDiag` …
SarahFrench Oct 10, 2025
fefdcee
Replace `errBackendInitCloud` custom error with `func errBackendInitC…
SarahFrench Oct 10, 2025
97319b5
Replace `errBackendWriteSaved` custom error with `func errBackendWrit…
SarahFrench Oct 10, 2025
795aea3
Add new usage of errBackendWriteSavedDiag; this is another place wher…
SarahFrench Oct 10, 2025
f4992ca
Add godoc comments
SarahFrench Oct 10, 2025
ee55409
Ensure errors aren't repeated in terminal output, and ensure warnings…
SarahFrench Oct 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 26 additions & 154 deletions internal/command/meta_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (m *Meta) selectWorkspace(b backend.Backend) error {
log.Printf("[TRACE] Meta.selectWorkspace: selecting the new HCP Terraform workspace requested by the user (%s)", name)
return m.SetWorkspace(name)
} else {
return errors.New(strings.TrimSpace(errBackendNoExistingWorkspaces))
return &errBackendNoExistingWorkspaces{}
}
}

Expand Down Expand Up @@ -341,7 +341,7 @@ func (m *Meta) BackendForLocalPlan(settings plans.Backend) (backendrun.Operation

f := backendInit.Backend(settings.Type)
if f == nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), settings.Type))
diags = diags.Append(errBackendSavedUnknown{settings.Type})
return nil, diags
}
b := f()
Expand Down Expand Up @@ -751,11 +751,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di

initReason := fmt.Sprintf("Unsetting the previously set backend %q", s.Backend.Type)
if !opts.Init {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Backend initialization required, please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
))
diags = diags.Append(errBackendInitDiag(initReason))
return nil, diags
}

Expand Down Expand Up @@ -787,18 +783,10 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
if !opts.Init {
if backendConfig.Type == "cloud" {
initReason := "Initial configuration of HCP Terraform or Terraform Enterprise"
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
))
diags = diags.Append(errBackendInitCloudDiag(initReason))
} else {
initReason := fmt.Sprintf("Initial configuration of the requested backend %q", backendConfig.Type)
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Backend initialization required, please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
))
diags = diags.Append(errBackendInitDiag(initReason))
}
return nil, diags
}
Expand Down Expand Up @@ -979,23 +967,11 @@ func (m *Meta) determineInitReason(previousBackendType string, currentBackendTyp
var diags tfdiags.Diagnostics
switch cloudMode {
case cloud.ConfigChangeInPlace:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
))
diags = diags.Append(errBackendInitCloudDiag(initReason))
case cloud.ConfigMigrationIn:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
))
diags = diags.Append(errBackendInitCloudDiag(initReason))
default:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Backend initialization required: please run \"terraform init\"",
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
))
diags = diags.Append(errBackendInitDiag(initReason))
}

return diags
Expand Down Expand Up @@ -1035,7 +1011,7 @@ func (m *Meta) backendFromState(_ context.Context) (backend.Backend, tfdiags.Dia
}
f := backendInit.Backend(s.Backend.Type)
if f == nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
diags = diags.Append(errBackendSavedUnknown{s.Backend.Type})
return nil, diags
}
b := f()
Expand Down Expand Up @@ -1158,11 +1134,11 @@ func (m *Meta) backend_c_r_S(
// Remove the stored metadata
s.Backend = nil
if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
diags = diags.Append(errBackendClearSaved{err})
return nil, diags
}
if err := sMgr.PersistState(); err != nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
diags = diags.Append(errBackendClearSaved{err})
return nil, diags
}

Expand Down Expand Up @@ -1195,21 +1171,22 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
}

workspaces, wDiags := localB.Workspaces()
diags = diags.Append(wDiags)
if wDiags.HasErrors() {
diags = diags.Append(fmt.Errorf(errBackendLocalRead, wDiags.Err()))
diags = diags.Append(wDiags.Warnings())
diags = diags.Append(&errBackendLocalRead{wDiags.Err()})
return nil, diags
}

var localStates []statemgr.Full
for _, workspace := range workspaces {
localState, sDiags := localB.StateMgr(workspace)
if sDiags.HasErrors() {
diags = diags.Append(fmt.Errorf(errBackendLocalRead, sDiags.Err()))
diags = diags.Append(sDiags.Warnings())
diags = diags.Append(&errBackendLocalRead{sDiags.Err()})
return nil, diags
}
if err := localState.RefreshState(); err != nil {
diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
diags = diags.Append(&errBackendLocalRead{err})
return nil, diags
}

Expand Down Expand Up @@ -1268,11 +1245,11 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
for _, localState := range localStates {
// We always delete the local state, unless that was our new state too.
if err := localState.WriteState(nil); err != nil {
diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
diags = diags.Append(&errBackendMigrateLocalDelete{err})
return nil, diags
}
if err := localState.PersistState(nil); err != nil {
diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
diags = diags.Append(&errBackendMigrateLocalDelete{err})
return nil, diags
}
}
Expand Down Expand Up @@ -1326,11 +1303,11 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
}

if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
diags = diags.Append(errBackendWriteSavedDiag(err))
return nil, diags
}
if err := sMgr.PersistState(); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
diags = diags.Append(errBackendWriteSavedDiag(err))
return nil, diags
}

Expand Down Expand Up @@ -1456,11 +1433,11 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
}

if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
diags = diags.Append(errBackendWriteSavedDiag(err))
return nil, diags
}
if err := sMgr.PersistState(); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
diags = diags.Append(errBackendWriteSavedDiag(err))
return nil, diags
}

Expand Down Expand Up @@ -1489,7 +1466,7 @@ func (m *Meta) savedBackend(sMgr *clistate.LocalState) (backend.Backend, tfdiags
// Get the backend
f := backendInit.Backend(s.Backend.Type)
if f == nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
diags = diags.Append(errBackendSavedUnknown{s.Backend.Type})
return nil, diags
}
b := f()
Expand Down Expand Up @@ -1543,8 +1520,9 @@ func (m *Meta) updateSavedBackendHash(cHash int, sMgr *clistate.LocalState) tfdi
if s.Backend.Hash != uint64(cHash) {
s.Backend.Hash = uint64(cHash)
if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(err)
diags = diags.Append(errBackendWriteSavedDiag(err))
}
// No need to call PersistState as it's a no-op
Comment on lines -1546 to +1525
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Highlighting this - this is a place where I have actually changed something about the output from Terraform. This error wasn't 'wrapped' in the same way that other errors (from WriteState or PersistState) were handled elsewhere in the code.

}

return diags
Expand Down Expand Up @@ -1788,7 +1766,7 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
// Get the backend
f := backendInit.Backend(c.Type)
if f == nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type))
diags = diags.Append(errBackendNewUnknown{c.Type})
return nil, cty.NilVal, diags
}
b := f()
Expand Down Expand Up @@ -2149,105 +2127,6 @@ func (m *Meta) GetStateStoreProviderFactory(config *configs.StateStore, locks *d
// Output constants and initialization code
//-------------------------------------------------------------------

const errBackendLocalRead = `
Error reading local state: %s

Terraform is trying to read your local state to determine if there is
state to migrate to your newly configured backend. Terraform can't continue
without this check because that would risk losing state. Please resolve the
error above and try again.
`

const errBackendMigrateLocalDelete = `
Error deleting local state after migration: %s

Your local state is deleted after successfully migrating it to the newly
configured backend. As part of the deletion process, a backup is made at
the standard backup path unless explicitly asked not to. To cleanly operate
with a backend, we must delete the local state file. Please resolve the
issue above and retry the command.
`

const errBackendNewUnknown = `
The backend %q could not be found.

This is the backend specified in your Terraform configuration file.
This error could be a simple typo in your configuration, but it can also
be caused by using a Terraform version that doesn't support the specified
backend type. Please check your configuration and your Terraform version.

If you'd like to run Terraform and store state locally, you can fix this
error by removing the backend configuration from your configuration.
`

const errBackendNoExistingWorkspaces = `
No existing workspaces.

Use the "terraform workspace" command to create and select a new workspace.
If the backend already contains existing workspaces, you may need to update
the backend configuration.
`

const errBackendSavedUnknown = `
The backend %q could not be found.

This is the backend that this Terraform environment is configured to use
both in your configuration and saved locally as your last-used backend.
If it isn't found, it could mean an alternate version of Terraform was
used with this configuration. Please use the proper version of Terraform that
contains support for this backend.

If you'd like to force remove this backend, you must update your configuration
to not use the backend and run "terraform init" (or any other command) again.
`

const errBackendClearSaved = `
Error clearing the backend configuration: %s

Terraform removes the saved backend configuration when you're removing a
configured backend. This must be done so future Terraform runs know to not
use the backend configuration. Please look at the error above, resolve it,
and try again.
`

const errBackendInit = `
Reason: %s

The "backend" is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you're using is using a custom configuration for
the Terraform backend.

Changes to backend configurations require reinitialization. This allows
Terraform to set up the new configuration, copy existing state, etc. Please run
"terraform init" with either the "-reconfigure" or "-migrate-state" flags to
use the current configuration.

If the change reason above is incorrect, please verify your configuration
hasn't changed and try again. At this point, no changes to your existing
configuration or state have been made.
`

const errBackendInitCloud = `
Reason: %s.

Changes to the HCP Terraform configuration block require reinitialization, to discover any changes to the available workspaces.

To re-initialize, run:
terraform init

Terraform has not yet made changes to your existing configuration or state.
`

const errBackendWriteSaved = `
Error saving the backend configuration: %s

Terraform saves the complete backend configuration in a local file for
configuring the backend on future operations. This cannot be disabled. Errors
are usually due to simple file permission errors. Please look at the error
above, resolve it, and try again.
`

const outputBackendMigrateChange = `
Terraform detected that the backend type changed from %q to %q.
`
Expand Down Expand Up @@ -2280,10 +2159,3 @@ const successBackendSet = `
Successfully configured the backend %q! Terraform will automatically
use this backend unless the backend configuration changes.
`

var migrateOrReconfigDiag = tfdiags.Sourceless(
tfdiags.Error,
"Backend configuration changed",
"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)
Loading
Loading