Repository: gruntwork-io/terragrunt (language: go)

Issue:
sops_decrypt_file
## Describe the bug

any call to sops_decrypt_file("wrong_file_name") fails with panic.
./terragrunt.hcl:6,14-37: Error in function call; Call to function "read_terragrunt_config" failed: ../terragrunt.hcl:27,28-46: Error in function call; Call to function "sops_decrypt_file" failed: panic in function implementation: runtime error: invalid memory address or nil pointer dereference

in case file is correct, there is no error. works as expected.

```hcl
secret_vars = yamldecode(sops_decrypt_file("secrets.yaml"))
```

## Versions

- Terragrunt version: 0.68.0..0.68.4

## Additional context

```
./terragrunt.hcl:6,14-37: Error in function call; Call to function "read_terragrunt_config" failed: ../terragrunt.hcl:27,28-46: Error in function call; Call to function "sops_decrypt_file" failed: panic in function implementation: runtime error: invalid memory address or nil pointer dereference
goroutine 9 [running]:
runtime/debug.Stack()
	/usr/local/go/src/runtime/debug/stack.go:26 +0x5e
github.com/zclconf/go-cty/cty/function.errorForPanic(...)
	../../../../../../../circleci/go/pkg/mod/github.com/zclconf/go-cty@v1.14.1/cty/function/error.go:44
github.com/zclconf/go-cty/cty/function.Function.Call.func2()
	../../../../../../../circleci/go/pkg/mod/github.com/zclconf/go-cty@v1.14.1/cty/function/function.go:336 +0x9b
panic({0x2028d80?, 0x3b0ce00?})
	/usr/local/go/src/runtime/panic.go:785 +0x132
github.com/gruntwork-io/terragrunt/internal/errors.(*MultiError).Len(...)
	../../../../../../../circleci/project/internal/errors/multierror.go:54
github.com/gruntwork-io/terragrunt/config.extractSopsErrors({0x286b6a0, 0xc000d197e0})
	../../../../../../../circleci/project/config/config_helpers.go:1113 +0x1e7
github.com/gruntwork-io/terragrunt/config.sopsDecryptFile(0xc000a22000, {0xc000a49f10?, 0xc000d19780?, 0xc000a4fae0?})
	../../../../../../../circleci/project/config/config_helpers.go:838 +0x50e
github.com/gruntwork-io/terragrunt/config.createTerragruntEvalContext.wrapStringSliceToStringAsFuncImpl.func23({0xc000d19780?, 0xc000130c41?, 0x1f2dcc0?}, {{0xc000a49f00?, 0x0?}})
	../../../../../../../circleci/project/config/cty_helpers.go:31 +0x4b
github.com/zclconf/go-cty/cty/function.Function.Call({0x2888ea0?}, {0xc000d19780, 0x1, 0x1})
	../../../../../../../circleci/go/pkg/mod/github.com/zclconf/go-cty@v1.14.1/cty/function/function.go:340 +0x6ee
github.com/hashicorp/hcl/v2/hclsyntax.(*FunctionCallExpr).Value(0xc000ab4c30, 0xc00012b7d0)
	../../../../../../../circleci/go/pkg/mod/github.com/hashicorp/hcl/v2@v2.19.1/hclsyntax/expression.go:456 +0x194f
github.com/hashicorp/hcl/v2/hclsyntax.(*FunctionCallExpr).Value(0xc000ab4d20, 0xc00012b7d0)
	../../../../../../../circleci/go/pkg/mod/github.com/hashicorp/hcl/v2@v2.19.1/hclsyntax/expression.go:421 +0x1184
github.com/gruntwork-io/terragrunt/config/hclparse.(*Attribute).Value(0xc000a49a00, 0xc000ab4d20?)
	../../../../../../../circleci/project/config/hclparse/attributes.go:77 +0x2c
github.com/gruntwork-io/terragrunt/config.attemptEvaluateLocals(0xc000a22000, 0xc000b73680, {0xc000a4da40, 0x1b, 0x0?}, 0xc000615ec0)
	../../../../../../../circleci/project/config/locals.go:131 +0x51a
github.com/gruntwork-io/terragrunt/config.EvaluateLocalsBlock(0xc000a22000, 0xc000b73680)
	../../../../../../../circleci/project/config/locals.go:58 +0x206
github.com/gruntwork-io/terragrunt/config.DecodeBaseBlocks(0xc000aff3b0, 0xc000b73680, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:129 +0x106
github.com/gruntwork-io/terragrunt/config.PartialParseConfig(0xc000aff340, 0xc000b73680, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:229 +0xcc
github.com/gruntwork-io/terragrunt/config.TerragruntConfigFromPartialConfig(0xc000aff340, 0xc000b73680, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:186 +0x3be
github.com/gruntwork-io/terragrunt/config.setIAMRole(0xc000aff2d0, 0xc000b73680, 0x0)
	../../../../../../../circleci/project/config/config.go:916 +0x31d
github.com/gruntwork-io/terragrunt/config.ParseConfig(0xc000aff260, 0xc000b73680, 0x0)
	../../../../../../../circleci/project/config/config.go:834 +0xa5
github.com/gruntwork-io/terragrunt/config.ParseConfigFile.func1({0xc000b267e0?, 0xc000b4f200?})
	../../../../../../../circleci/project/config/config.go:774 +0x3d8
github.com/gruntwork-io/terragrunt/telemetry.Time({0x28888a8?, 0xc000aff260?}, {0x23a182b?, 0x2888570?}, 0xc000afc3c0?, 0x1f2e6c0?)
	../../../../../../../circleci/project/telemetry/metrics.go:42 +0x293
github.com/gruntwork-io/terragrunt/config.ParseConfigFile.Telemetry.func2({0x28888a8?, 0xc000aff260?})
	../../../../../../../circleci/project/telemetry/telemetry.go:80 +0x2d
github.com/gruntwork-io/terragrunt/telemetry.Trace({0x28888a8?, 0xc000aff260?}, {0x23a182b?, 0xb?}, 0x39a2900?, 0x400?)
	../../../../../../../circleci/project/telemetry/traces.go:38 +0x14f
github.com/gruntwork-io/terragrunt/telemetry.Telemetry(...)
	../../../../../../../circleci/project/telemetry/telemetry.go:79
github.com/gruntwork-io/terragrunt/config.ParseConfigFile(0xc000aff260, {0xc000ae06c0, 0x57}, 0x0)
	../../../../../../../circleci/project/config/config.go:731 +0x2a5
github.com/gruntwork-io/terragrunt/config.ParseTerragruntConfig(0xc000afefc0, {0xc000ae06c0?, 0xc000130c41?}, 0x0)
	../../../../../../../circleci/project/config/config_helpers.go:640 +0x1ad
github.com/gruntwork-io/terragrunt/config.createTerragruntEvalContext.readTerragruntConfigAsFuncImpl.func6({0xc000b72480, 0x1, 0x1f2dcc0?}, {{0xc000b8a050?, 0xc0007fa1a0?}})
	../../../../../../../circleci/project/config/config_helpers.go:687 +0x105
github.com/zclconf/go-cty/cty/function.Function.Call({0x2888ea0?}, {0xc000b72480, 0x1, 0x1})
	../../../../../../../circleci/go/pkg/mod/github.com/zclconf/go-cty@v1.14.1/cty/function/function.go:340 +0x6ee
github.com/hashicorp/hcl/v2/hclsyntax.(*FunctionCallExpr).Value(0xc000ab4a50, 0xc000b802a0)
	../../../../../../../circleci/go/pkg/mod/github.com/hashicorp/hcl/v2@v2.19.1/hclsyntax/expression.go:456 +0x194f
github.com/gruntwork-io/terragrunt/config/hclparse.(*Attribute).Value(0xc0007dde30, 0xc000ab4a50?)
	../../../../../../../circleci/project/config/hclparse/attributes.go:77 +0x2c
github.com/gruntwork-io/terragrunt/config.attemptEvaluateLocals(0xc000afefc0, 0xc000a99580, {0xc0007ed9c8, 0x3, 0x0?}, 0xc000b4ef00)
	../../../../../../../circleci/project/config/locals.go:131 +0x51a
github.com/gruntwork-io/terragrunt/config.EvaluateLocalsBlock(0xc000afefc0, 0xc000a99580)
	../../../../../../../circleci/project/config/locals.go:58 +0x206
github.com/gruntwork-io/terragrunt/config.DecodeBaseBlocks(0xc000afee00, 0xc000a99580, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:129 +0x106
github.com/gruntwork-io/terragrunt/config.PartialParseConfig(0xc000afed20, 0xc000a99580, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:229 +0xcc
github.com/gruntwork-io/terragrunt/config.TerragruntConfigFromPartialConfig(0xc000afed20, 0xc000a99580, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:186 +0x3be
github.com/gruntwork-io/terragrunt/config.PartialParseConfigFile(0xc000afed20, {0xc000b0a1c0, 0x69}, 0x0)
	../../../../../../../circleci/project/config/config_partial.go:164 +0x1bf
github.com/gruntwork-io/terragrunt/cli/commands/terraform.checkVersionConstraints({0x2888570, 0xc000614720}, 0xc000a18408)
	../../../../../../../circleci/project/cli/commands/terraform/version_check.go:40 +0xfb
github.com/gruntwork-io/terragrunt/cli/commands/terraform.runTerraform({0x2888570, 0xc000614720}, 0xc000a18408, 0xc000bdfed8)
	../../../../../../../circleci/project/cli/commands/terraform/action.go:97 +0x146
github.com/gruntwork-io/terragrunt/cli/commands/terraform.Run({0x2888570?, 0xc000614720?}, 0xc0007ef0c0?)
	../../../../../../../circleci/project/cli/commands/terraform/action.go:83 +0x52
github.com/gruntwork-io/terragrunt/cli.NewApp.NewCommand.Action.func5(0xc0007ef0c0)
	../../../../../../../circleci/project/cli/commands/terraform/command.go:47 +0xad
github.com/gruntwork-io/terragrunt/cli.runAction.func2()
	../../../../../../../circleci/project/cli/app.go:235 +0x47
golang.org/x/sync/errgroup.(*Group).Go.func1()
	../../../../../../../circleci/go/pkg/mod/golang.org/x/sync@v0.8.0/errgroup/errgroup.go:78 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 1
	../../../../../../../circleci/go/pkg/mod/golang.org/x/sync@v0.8.0/errgroup/errgroup.go:75 +0x96
```


Discussion / hints:

Fix patch:
diff --git a/config/config_helpers.go b/config/config_helpers.go
index f8e4a9fc75..f225670c00 100644
--- a/config/config_helpers.go
+++ b/config/config_helpers.go
@@ -1110,7 +1110,7 @@ func extractSopsErrors(err error) *errors.MultiError {
 	}
 
 	// append the original error if no group results were found
-	if errs.Len() == 0 {
+	if errs == nil {
 		errs = errs.Append(err)
 	}
 


Test patch:
diff --git a/test/fixtures/sops-missing/main.tf b/test/fixtures/sops-missing/main.tf
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/test/fixtures/sops-missing/terragrunt.hcl b/test/fixtures/sops-missing/terragrunt.hcl
new file mode 100644
index 0000000000..c130ba2f77
--- /dev/null
+++ b/test/fixtures/sops-missing/terragrunt.hcl
@@ -0,0 +1,3 @@
+locals {
+  secret_vars = yamldecode(sops_decrypt_file("${get_terragrunt_dir()}/missing.yaml"))
+}
diff --git a/test/integration_sops_test.go b/test/integration_sops_test.go
index d899ef1ee2..a19cc91f8a 100644
--- a/test/integration_sops_test.go
+++ b/test/integration_sops_test.go
@@ -12,8 +12,9 @@ import (
 )
 
 const (
-	testFixtureSops       = "fixtures/sops"
-	testFixtureSopsErrors = "fixtures/sops-errors"
+	testFixtureSops        = "fixtures/sops"
+	testFixtureSopsErrors  = "fixtures/sops-errors"
+	testFixtureSopsMissing = "fixtures/sops-missing"
 )
 
 func TestSopsDecryptedCorrectly(t *testing.T) {
@@ -97,3 +98,17 @@ func TestTerragruntLogSopsErrors(t *testing.T) {
 	assert.Contains(t, errorOut, "error decrypting key: [error decrypting key")
 	assert.Contains(t, errorOut, "error base64-decoding encrypted data key: illegal base64 data at input byte")
 }
+
+func TestSopsDecryptOnMissing(t *testing.T) {
+	t.Parallel()
+
+	cleanupTerraformFolder(t, testFixtureSopsMissing)
+	tmpEnvPath := copyEnvironment(t, testFixtureSopsMissing)
+	rootPath := util.JoinPath(tmpEnvPath, testFixtureSopsMissing)
+
+	// apply and check for errors
+	_, errorOut, err := runTerragruntCommandWithOutput(t, "terragrunt apply --terragrunt-non-interactive --terragrunt-log-level debug --terragrunt-working-dir "+rootPath)
+	require.Error(t, err)
+
+	assert.Contains(t, errorOut, "Encountered error while evaluating locals in file ./terragrunt.hcl")
+}