{"record":{"id":"3373c2c48fe77ce0","repo":"hashicorp/terraform","slug":"cannot-create-temporary-file-to-update-credentials","errorCode":null,"errorMessage":"cannot create temporary file to update credentials: %s","messagePattern":"cannot create temporary file to update credentials: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/cliconfig/credentials.go","lineNumber":400,"sourceCode":"\t}\n\n\tnewSrc, err := json.MarshalIndent(raw, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot serialize updated credentials file: %s\", err)\n\t}\n\n\t// Now we'll write our new content over the top of the existing file.\n\t// Because we updated the data structure surgically here we should not\n\t// have disturbed the meaning of any other content in the file, but it\n\t// might have a different JSON layout than before.\n\t// We'll create a new file with a different name first and then rename\n\t// it over the old file in order to make the change as atomically as\n\t// the underlying OS/filesystem will allow.\n\t{\n\t\tdir, file := filepath.Split(filename)\n\t\tf, err := ioutil.TempFile(dir, file)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot create temporary file to update credentials: %s\", err)\n\t\t}\n\t\ttmpName := f.Name()\n\t\tmoved := false\n\t\tdefer func(f *os.File, name string) {\n\t\t\t// Remove the temporary file if it hasn't been moved yet. We're\n\t\t\t// ignoring errors here because there's nothing we can do about\n\t\t\t// them anyway.\n\t\t\tif !moved {\n\t\t\t\tos.Remove(name)\n\t\t\t}\n\t\t}(f, tmpName)\n\n\t\t// Write the credentials to the temporary file, then immediately close\n\t\t// it, whether or not the write succeeds.\n\t\t_, err = f.Write(newSrc)\n\t\tf.Close()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot write to temporary file %s: %s\", tmpName, err)","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/command/cliconfig/credentials.go#L382-L418","documentation":"Returned by StoreHostCredentialsEntry (and callers like the credentials helper for `terraform login`/`logout`) when ioutil.TempFile fails while staging an atomic rewrite of the Terraform credentials file (~/.terraform.d/credentials.json). The code intentionally writes to a sibling temp file in the same directory then renames it over the original so a crash never leaves a truncated JSON credentials file. A failure here means Terraform could not even begin that safe write.","triggerScenarios":"ioutil.TempFile(dir, file) at credentials.go:398 returns an error, where dir is the directory portion of the active credentials filename and file is its base name (used as the temp prefix). This happens when the credentials directory does not exist, is not writable by the current user, is on a read-only filesystem, has no free inodes, or the process lacks permission.","commonSituations":"HOME is unset or points to a non-existent/unwritable directory (common in containers, CI, or hardened sandboxes); the user ran Terraform as root and the .terraform.d directory is now owned by root; a read-only bind-mount of the home directory; an antivirus/EDR agent on Windows blocking temp file creation; an HPC environment with per-user inode quotas exhausted.","solutions":["Check that the directory holding the credentials file exists and is writable: `ls -ld ~/.terraform.d` and `touch ~/.terraform.d/.write-test`.","Ensure HOME (or USERPROFILE on Windows) is set to a real, writable directory inside the container/CI job and export it explicitly.","Fix ownership if a previous `sudo terraform` created root-owned files: `sudo chown -R $USER ~/.terraform.d`.","If on a read-only or quota-limited filesystem, point TF_CLI_CONFIG_FILE / TF_DATA_DIR at a writable location or mount a writable volume for credentials.","Free inodes/disk space on the volume holding the home directory (`df -i`, `df -h`)."],"exampleFix":"# before: container launched without a writable HOME\ndocker run --rm -it myimg terraform login\n# cannot create temporary file to update credentials: open .../credentials.json123456: permission denied\n\n# after: give the container a writable home and data dir\ndocker run --rm -it -e HOME=/tmp/tf -v tfdata:/tmp/tf/.terraform.d myimg terraform login","handlingStrategy":"try-catch","validationCode":"// Validate the credentials directory is writable before calling StoreHostCredentialsEntry\nif fi, err := os.Stat(filepath.Dir(credsPath)); err != nil || !fi.IsDir() {\n    return fmt.Errorf(\"credentials dir not usable: %w\", err)\n}\nif err := os.WriteFile(filepath.Join(filepath.Dir(credsPath), \".write-probe\"), []byte(\"\"), 0600); err != nil {\n    return fmt.Errorf(\"credentials dir not writable: %w\", err)\n}\nos.Remove(filepath.Join(filepath.Dir(credsPath), \".write-probe\"))","typeGuard":null,"tryCatchPattern":"// Treat any error from StoreHostCredentialsEntry as a non-fatal credentials failure and continue without persisting the credential (e.g. fall back to env TF_TOKEN_*).\nif err := credsStore.StoreHostCredentialsEntry(host, token); err != nil {\n    if errors.Is(err, fs.ErrPermission) || strings.Contains(err.Error(), \"permission denied\") {\n        log.Printf(\"warning: cannot persist credential for %s (%v); set TF_TOKEN_%s instead\", host, err, strings.ReplaceAll(strings.ToUpper(host), \".\", \"_\"))\n    } else {\n        return err\n    }\n}","preventionTips":["Ensure HOME points to a writable, existing directory in containers/CI.","Never run terraform credentials commands as root unless the home dir is also root-owned.","Probe the credentials directory for write access during environment setup, not at first login.","Document TF_CLI_CONFIG_FILE as the escape hatch for read-only home setups."],"tags":["filesystem","credentials","atomic-write","configuration","permissions"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}