hashicorp/terraform · error

could not write lock info for %q: %s

Error message

could not write lock info for %q: %s

What it means

Returned by LocalState.writeLockInfo (local_state.go:305) when ioutil.WriteFile fails to create/overwrite the lock info metadata file (.terraform.tfstate.lock.info) with mode 0600. writeLockInfo is the final step of Lock after the OS-level file lock (s.lock) succeeded; it persists the LockInfo record so other processes can identify the lock holder. Failure here means the lock is held but not documented.

Source

Thrown at internal/command/clistate/local_state.go:312

	}

	info := statemgr.LockInfo{}
	err = json.Unmarshal(infoData, &info)
	if err != nil {
		return nil, fmt.Errorf("state file %q locked, but could not unmarshal lock info: %s", s.Path, err)
	}
	return &info, nil
}

// write a new lock info file
func (s *LocalState) writeLockInfo(info *statemgr.LockInfo) error {
	path := s.lockInfoPath()
	info.Path = s.Path
	info.Created = time.Now().UTC()

	err := ioutil.WriteFile(path, info.Marshal(), 0600)
	if err != nil {
		return fmt.Errorf("could not write lock info for %q: %s", s.Path, err)
	}
	return nil
}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Check write permission on the directory containing the state file: `ls -ld $(dirname <statefile>)` and `touch <dir>/.write-test`.
  2. Ensure the state directory exists and is writable by the Terraform process: `mkdir -p .terraform && chmod u+w .terraform`.
  3. Free disk space if the volume is full.
  4. If the directory was deleted mid-run, recreate it and re-run; call `terraform force-unlock` to clean up any half-acquired lock before retrying.

Example fix

# before
$ terraform plan
could not write lock info for "terraform.tfstate": open .terraform.tfstate.lock.info: permission denied

# after
$ chmod u+w .
$ terraform force-unlock <id>   # clear any partial lock
$ terraform plan
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the state directory is writable before locking
stateDir := filepath.Dir(s.Path)
if err := os.MkdirAll(stateDir, 0755); err != nil {
    return fmt.Errorf("state dir unusable: %w", err)
}
probe := filepath.Join(stateDir, ".write-probe")
if err := ioutil.WriteFile(probe, []byte(""), 0600); err != nil {
    return fmt.Errorf("state dir not writable: %w", err)
}
os.Remove(probe)

Try / catch

// If writeLockInfo fails after acquiring the OS lock, attempt to release the OS lock to avoid a held-but-undocumented lock.
if err := s.writeLockInfo(info); err != nil {
    if uerr := s.unlock(); uerr != nil {
        log.Printf("warning: failed to write lock info (%v) and failed to roll back OS lock (%v)", err, uerr)
    }
    s.lockID = ""
    return fmt.Errorf("could not persist lock info: %w", err)
}

Prevention

When it happens

Trigger: ioutil.WriteFile returns an error: the state directory is read-only or missing, permission denied on the directory, disk full, path too long, or the process lost write permission between acquiring the file lock and writing the metadata.

Common situations: The state directory is on a read-only mount; another user owns the directory; the directory was deleted between locking and writing info; disk filled up; running in a sandbox that allows file locking but not file creation.

Related errors


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