hashicorp/terraform · error

cannot create temporary file to update credentials: %s

Error message

cannot create temporary file to update credentials: %s

What it means

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.

Source

Thrown at internal/command/cliconfig/credentials.go:400

	}

	newSrc, err := json.MarshalIndent(raw, "", "  ")
	if err != nil {
		return fmt.Errorf("cannot serialize updated credentials file: %s", err)
	}

	// Now we'll write our new content over the top of the existing file.
	// Because we updated the data structure surgically here we should not
	// have disturbed the meaning of any other content in the file, but it
	// might have a different JSON layout than before.
	// We'll create a new file with a different name first and then rename
	// it over the old file in order to make the change as atomically as
	// the underlying OS/filesystem will allow.
	{
		dir, file := filepath.Split(filename)
		f, err := ioutil.TempFile(dir, file)
		if err != nil {
			return fmt.Errorf("cannot create temporary file to update credentials: %s", err)
		}
		tmpName := f.Name()
		moved := false
		defer func(f *os.File, name string) {
			// Remove the temporary file if it hasn't been moved yet. We're
			// ignoring errors here because there's nothing we can do about
			// them anyway.
			if !moved {
				os.Remove(name)
			}
		}(f, tmpName)

		// Write the credentials to the temporary file, then immediately close
		// it, whether or not the write succeeds.
		_, err = f.Write(newSrc)
		f.Close()
		if err != nil {
			return fmt.Errorf("cannot write to temporary file %s: %s", tmpName, err)

View on GitHub (pinned to c9def3e214)

Solutions

  1. Check that the directory holding the credentials file exists and is writable: `ls -ld ~/.terraform.d` and `touch ~/.terraform.d/.write-test`.
  2. Ensure HOME (or USERPROFILE on Windows) is set to a real, writable directory inside the container/CI job and export it explicitly.
  3. Fix ownership if a previous `sudo terraform` created root-owned files: `sudo chown -R $USER ~/.terraform.d`.
  4. 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.
  5. Free inodes/disk space on the volume holding the home directory (`df -i`, `df -h`).

Example fix

# before: container launched without a writable HOME
docker run --rm -it myimg terraform login
# cannot create temporary file to update credentials: open .../credentials.json123456: permission denied

# after: give the container a writable home and data dir
docker run --rm -it -e HOME=/tmp/tf -v tfdata:/tmp/tf/.terraform.d myimg terraform login
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the credentials directory is writable before calling StoreHostCredentialsEntry
if fi, err := os.Stat(filepath.Dir(credsPath)); err != nil || !fi.IsDir() {
    return fmt.Errorf("credentials dir not usable: %w", err)
}
if err := os.WriteFile(filepath.Join(filepath.Dir(credsPath), ".write-probe"), []byte(""), 0600); err != nil {
    return fmt.Errorf("credentials dir not writable: %w", err)
}
os.Remove(filepath.Join(filepath.Dir(credsPath), ".write-probe"))

Try / catch

// 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_*).
if err := credsStore.StoreHostCredentialsEntry(host, token); err != nil {
    if errors.Is(err, fs.ErrPermission) || strings.Contains(err.Error(), "permission denied") {
        log.Printf("warning: cannot persist credential for %s (%v); set TF_TOKEN_%s instead", host, err, strings.ReplaceAll(strings.ToUpper(host), ".", "_"))
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/3373c2c48fe77ce0. Report an issue: GitHub.