From 1f6b65711028661c0531d9064ccf7adbc9b48677 Mon Sep 17 00:00:00 2001 From: Sarah French Date: Fri, 21 Nov 2025 18:23:25 +0000 Subject: [PATCH] fix: Make `state show` command exit with code 1 if the stored state cannot be marshalled for rendering Prior to this change, when running a `state show` command with an address for a resource that existed in state but didn't match the current schema of the resource, users would see the "Failed to marshal" error but would also see output saying "The state file is empty. No resources are represented". This is misleading, as the state file isn't empty. Also, the command would exit with code 0 despite the error. --- .changes/v1.15/BUG FIXES-20251121-183045.yaml | 5 ++ internal/command/state_show.go | 1 + internal/command/state_show_test.go | 63 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 .changes/v1.15/BUG FIXES-20251121-183045.yaml diff --git a/.changes/v1.15/BUG FIXES-20251121-183045.yaml b/.changes/v1.15/BUG FIXES-20251121-183045.yaml new file mode 100644 index 000000000000..fca86c0bf70e --- /dev/null +++ b/.changes/v1.15/BUG FIXES-20251121-183045.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: '`state show`: The `state show` command will now explicitly fail and return code 1 when it fails to render the named resources state' +time: 2025-11-21T18:30:45.571448Z +custom: + Issue: "37933" diff --git a/internal/command/state_show.go b/internal/command/state_show.go index 61f09d8ae27c..6374ec51af0e 100644 --- a/internal/command/state_show.go +++ b/internal/command/state_show.go @@ -149,6 +149,7 @@ func (c *StateShowCommand) Run(args []string) int { root, outputs, err := jsonstate.MarshalForRenderer(statefile.New(singleInstance, "", 0), schemas) if err != nil { c.Streams.Eprintf("Failed to marshal state to json: %s", err) + return 1 } jstate := jsonformat.State{ diff --git a/internal/command/state_show_test.go b/internal/command/state_show_test.go index e978bed164e2..de8386795e6a 100644 --- a/internal/command/state_show_test.go +++ b/internal/command/state_show_test.go @@ -76,6 +76,69 @@ func TestStateShow(t *testing.T) { } } +func TestStateShow_errorMarshallingState(t *testing.T) { + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo_invalid", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + // The error is caused by the state containing attributes that don't + // match the schema for the resource. + AttrsJSON: []byte(`{"non_existent_attr":"I'm gonna cause an error!"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, state) + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Body: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "bar": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + + streams, done := terminal.StreamsForTesting(t) + c := &StateShowCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Streams: streams, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo_invalid", + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("unexpected code: %d\n\n%s", code, output.Stdout()) + } + + // Test that error outputs were displayed + expected := "unsupported attribute \"non_existent_attr\"" + actual := output.Stderr() + if !strings.Contains(actual, expected) { + t.Fatalf("Expected stderr output to include:\n%q\n\n Instead got:\n%q", expected, actual) + } +} + func TestStateShow_multi(t *testing.T) { submod, _ := addrs.ParseModuleInstanceStr("module.sub") state := states.BuildState(func(s *states.SyncState) {