{"record":{"id":"f1c6cf44f2b4f115","repo":"hashicorp/terraform","slug":"failed-to-read-state-file-attrs-from-v-v","errorCode":null,"errorMessage":"Failed to read state file attrs from %v: %v","messagePattern":"Failed to read state file attrs from (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/gcs/client.go","lineNumber":52,"sourceCode":"\tctx := context.TODO()\n\tstateFileReader, err := c.stateFile().NewReader(ctx)\n\tif err != nil {\n\t\tif err == storage.ErrObjectNotExist {\n\t\t\treturn nil, diags\n\t\t} else {\n\t\t\treturn nil, diags.Append(fmt.Errorf(\"Failed to open state file at %v: %v\", c.stateFileURL(), err))\n\t\t}\n\t}\n\tdefer stateFileReader.Close()\n\n\tstateFileContents, err := ioutil.ReadAll(stateFileReader)\n\tif err != nil {\n\t\treturn nil, diags.Append(fmt.Errorf(\"Failed to read state file from %v: %v\", c.stateFileURL(), err))\n\t}\n\n\tstateFileAttrs, err := c.stateFile().Attrs(ctx)\n\tif err != nil {\n\t\treturn nil, diags.Append(fmt.Errorf(\"Failed to read state file attrs from %v: %v\", c.stateFileURL(), err))\n\t}\n\n\tresult := &remote.Payload{\n\t\tData: stateFileContents,\n\t\tMD5:  stateFileAttrs.MD5,\n\t}\n\n\treturn result, diags\n}\n\nfunc (c *remoteClient) Put(data []byte) tfdiags.Diagnostics {\n\tvar diags tfdiags.Diagnostics\n\tctx := context.TODO()\n\terr := func() error {\n\t\tstateFileWriter := c.stateFile().NewWriter(ctx)\n\t\tif len(c.kmsKeyName) > 0 {\n\t\t\tstateFileWriter.KMSKeyName = c.kmsKeyName\n\t\t}","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/gcs/client.go#L34-L70","documentation":"Thrown by Get after the state bytes are read, when stateFile().Attrs(ctx) fails. The Attrs call fetches object metadata (used for the MD5 in remote.Payload); its failure means the metadata GET errored even though the content GET succeeded.","triggerScenarios":"Attrs returns an error — transient GCS metadata API failure, permission to read content but not metadata (rare but possible with custom roles), or a race where the object was deleted between the read and the attrs call.","commonSituations":"Concurrent `terraform apply` from another runner deletes the object mid-get; intermittent metadata API errors; custom IAM role granting objects.get but not objects.getIamPolicy-style metadata access.","solutions":["Retry the operation — metadata errors are usually transient.","Ensure no concurrent process is deleting the state object.","Verify the service account has standard objectViewer/objectAdmin roles rather than a custom subset.","Inspect the wrapped %v to confirm the failure type."],"exampleFix":"// no code change — retry; if persistent, eliminate concurrent state mutations","handlingStrategy":"retry","validationCode":"// Pre-flight: confirm objectViewer role (covers objects.get + metadata).","typeGuard":null,"tryCatchPattern":"// Retry the Attrs call; metadata-only failures are usually transient.\nvar attrs *storage.ObjectAttrs\nfor i := 0; i < 3; i++ {\n    attrs, err = f.Attrs(ctx)\n    if err == nil { break }\n    if !isTransient(err) { return err }\n}","preventionTips":["Prefer standard IAM roles (objectViewer/objectAdmin) over narrow custom roles.","Eliminate concurrent state deletions that race the attrs read.","Treat wrapped 5xx as retryable."],"tags":["gcs","backend","storage","metadata","state-read","transient"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}