{"record":{"id":"b593aa250fca7bc6","repo":"hashicorp/terraform","slug":"strings-join-errs-n","errorCode":null,"errorMessage":"strings.Join(errs, \"\\n\")","messagePattern":"strings\\.Join\\(errs, \"\\\\n\"\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloud/backend_common.go","lineNumber":749,"sourceCode":"\t\treturn nil\n\t}\n\n\tvar errs []string\n\tvar err error\n\n\tswitch r.StatusCode {\n\tcase 401:\n\t\treturn tfe.ErrUnauthorized\n\tcase 404:\n\t\treturn tfe.ErrResourceNotFound\n\t}\n\n\terrs, err = decodeErrorPayload(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errors.New(strings.Join(errs, \"\\n\"))\n}\n\nfunc decodeErrorPayload(r *http.Response) ([]string, error) {\n\t// Decode the error payload.\n\tvar errs []string\n\terrPayload := &jsonapi.ErrorsPayload{}\n\terr := json.NewDecoder(r.Body).Decode(errPayload)\n\tif err != nil || len(errPayload.Errors) == 0 {\n\t\treturn errs, errors.New(r.Status)\n\t}\n\n\t// Parse and format the errors.\n\tfor _, e := range errPayload.Errors {\n\t\tif e.Detail == \"\" {\n\t\t\terrs = append(errs, e.Title)\n\t\t} else {\n\t\t\terrs = append(errs, fmt.Sprintf(\"%s\\n\\n%s\", e.Title, e.Detail))\n\t\t}","sourceCodeStart":731,"sourceCodeEnd":767,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/cloud/backend_common.go#L731-L767","documentation":"This is the generic error factory in the cloud backend's HTTP-error decoder (cloud/backend_common.go:749). For TFE/HCP API responses that are not 401 (ErrUnauthorized) or 404 (ErrResourceNotFound), it decodes the JSON:api error payload and joins the per-error titles/details with newlines via strings.Join(errs, \"\\n\"). You see this message when the API returned an error body with one or more titled errors (validation failures, conflicts, server errors).","triggerScenarios":"Any TFE/HCP API call made by the cloud backend that returns a non-2xx, non-401/404 response with a parseable JSON:api error payload — e.g. 422 workspace validation errors, 409 conflicts, 500s with structured errors — flows through decodeErrorPayload and is re-emitted as errors.Join of the titles.","commonSituations":"Workspace name/tag conflicts, invalid run options, rate limiting with a titled error body, malformed state uploads, plan/apply API validation rejections.","solutions":["Read the joined error titles — they echo the TFC/HCP API error text and identify the failing field/constraint.","Fix the specific config value or API argument the API rejected, then retry.","If the error is transient (5xx, rate-limit), retry after a short backoff."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := b.client.Workspaces.Update(ctx, org, name, opts)\nif err != nil {\n    // cloud backend joins titled API errors with newlines\n    for _, line := range strings.Split(err.Error(), \"\\n\") {\n        log.Printf(\"tfe api error: %s\", line)\n    }\n    return err\n}","preventionTips":["Parse the joined newline-delimited titles to find the failing field.","Correct the rejected argument before retrying.","Use exponential backoff for transient 5xx/429 bodies."],"tags":["terraform","cloud-backend","tfe","http","api","error-handling"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}