gastownhall/beads · error

failed to create temp file: %w

Error message

failed to create temp file: %w

What it means

atomicWriteFile calls os.CreateTemp in the target file's directory to stage content before an atomic rename. This error wraps CreateTemp failure — the directory doesn't exist, isn't writable, or the OS refused temp-file creation.

Source

Thrown at cmd/bd/backup_export.go:100

// atomicWriteFile writes data to a same-directory temp file, fsyncs the
// temp file's own contents, then renames it into place. This avoids a
// truncated/partial file at path if the process crashes mid-write.
//
// Two caveats this does NOT cover, narrowing the "crash-safe" claim rather
// than the implementation (existing callers' behavior is unchanged here):
//   - Only the temp file's contents are fsynced, not the parent directory
//     entry; a crash between the rename and a subsequent directory fsync
//     can still lose the rename itself on some filesystems.
//   - os.Rename's atomic-replace guarantee is a POSIX/Unix property; it is
//     not guaranteed on Windows. It also does not follow a symlink at
//     path — it replaces whatever is there, symlink or not — so a caller
//     that must preserve a symlink's target should resolve path with
//     filepath.EvalSymlinks first (see cmd/bd/proxied_server.go).
func atomicWriteFile(path string, data []byte) error {
	dir := filepath.Dir(path)
	tmp, err := os.CreateTemp(dir, ".backup-tmp-*")
	if err != nil {
		return fmt.Errorf("failed to create temp file: %w", err)
	}
	tmpPath := tmp.Name()

	if _, err := tmp.Write(data); err != nil {
		_ = tmp.Close()
		_ = os.Remove(tmpPath)
		return fmt.Errorf("failed to write temp file: %w", err)
	}
	if err := tmp.Sync(); err != nil {
		_ = tmp.Close()
		_ = os.Remove(tmpPath)
		return fmt.Errorf("failed to sync temp file: %w", err)
	}
	if err := tmp.Close(); err != nil {
		_ = os.Remove(tmpPath)
		return fmt.Errorf("failed to close temp file: %w", err)
	}
	if err := os.Rename(tmpPath, path); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Ensure the target directory exists: mkdir -p .beads (atomicWriteFile does not create parent dirs).
  2. Check write permission on the directory: ls -ld .beads and chmod/chown appropriately.
  3. Check disk space and inodes: df -h and df -i.
  4. If on a read-only mount, remount read/write or point BD_DIR at a writable location.

Example fix

// shell
mkdir -p .beads && chmod u+w .beads && bd sync
Defensive patterns

Strategy: validation

Validate before calling

dir := ".beads"
info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
    os.MkdirAll(dir, 0o755)
}
// probe writability with a real temp create (same mechanism bd uses)
if f, err := os.CreateTemp(dir, ".probe-*"); err == nil { f.Close(); os.Remove(f.Name()) }

Type guard

func dirIsWritable(dir string) bool {
    f, err := os.CreateTemp(dir, ".probe-*")
    if err != nil { return false }
    f.Close(); os.Remove(f.Name())
    return true
}

Try / catch

if err := saveBackupState(dir, state); err != nil {
    if strings.Contains(err.Error(), "create temp file") {
        if mkErr := os.MkdirAll(dir, 0o755); mkErr == nil {
            err = saveBackupState(dir, state) // one retry after ensuring dir
        }
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: os.CreateTemp(filepath.Dir(path), ".backup-tmp-*") fails: parent directory missing (e.g. .beads/ deleted), read-only filesystem, disk full of inodes, or permission denied on the directory.

Common situations: .beads directory removed while bd ran; running inside a read-only container mount; full disk/inode exhaustion; wrong user without write access to .beads; TMP restrictions on hardened systems.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/049cdd2775495652. Report an issue: GitHub.