gastownhall/beads · error

ensureProxiedServerConfig: write %s: %w

Error message

ensureProxiedServerConfig: write %s: %w

What it means

The final step of ensureProxiedServerConfig writes the rendered YAML atomically via atomicWriteFile(resolveConfigWriteTarget(path), body); any write failure is wrapped here. resolveConfigWriteTarget resolves the path to its physical location first because os.Rename's destination does not follow symlinks — so this error covers temp-file creation, write, fsync, or rename failures at the real destination.

Source

Thrown at cmd/bd/proxied_server.go:158

	switch _, err := os.Stat(path); {
	case err == nil:
		return path, nil
	case !os.IsNotExist(err):
		return "", fmt.Errorf("ensureProxiedServerConfig: stat %s: %w", path, err)
	}

	port, err := proxy.PickFreePort()
	if err != nil {
		return "", fmt.Errorf("ensureProxiedServerConfig: pick free port: %w", err)
	}

	body, err := renderProxiedServerConfig(port)
	if err != nil {
		return "", fmt.Errorf("ensureProxiedServerConfig: render YAML: %w", err)
	}
	if err := atomicWriteFile(resolveConfigWriteTarget(path), body); err != nil {
		return "", fmt.Errorf("ensureProxiedServerConfig: write %s: %w", path, err)
	}
	return path, nil
}

// resolveConfigWriteTarget resolves path to its physical location before
// an atomic rewrite. os.Rename's destination argument does not follow
// symlinks — it unlinks and replaces whatever is AT that path, symlink or
// not — so writing straight to a symlinked config.yaml would silently
// replace the symlink itself with a regular file instead of updating the
// file it points at. Falls back to path unresolved when it does not exist
// yet (filepath.EvalSymlinks errors on a missing path), which covers both
// the ordinary "no config.yaml yet" case and a dangling symlink.
func resolveConfigWriteTarget(path string) string {
	resolved, err := filepath.EvalSymlinks(path)
	if err != nil {
		return path
	}
	return resolved

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped errno: ENOSPC/quota → free space (`df -h <dir>`, `du -sh ~/.beads`) or raise the quota; EACCES → fix ownership of the config directory (`ls -ld ~/.beads`, `sudo chown -R $USER ~/.beads` if root-owned).
  2. If the filesystem is read-only (container overlay, immutable mount), remount rw or relocate the config to a writable path via a custom --config.
  3. Disable/repair interference from sync daemons: pause Dropbox/backup agents or move ~/.beads off the synced/network mount.
  4. Check LSM denials (SELinux audit log / AppArmor) and adjust policy if writes to the path are being blocked.
  5. Rerun the command after fixing conditions — the atomic write either succeeds fully or leaves the old file intact, so retry is safe.

Example fix

// before
$ ls -ld ~/.beads
drwx------ root root ~/.beads    # root-owned after sudo run
// after
$ sudo chown -R $USER:$USER ~/.beads && bd serve
Defensive patterns

Strategy: try-catch

Validate before calling

dir := filepath.Dir(cfgPath)
if err := unix.Access(dir, unix.W_OK); err != nil {
	return fmt.Errorf("cannot write config dir %s: %w", dir, err)
}

Try / catch

if err := atomicWriteFile(target, body); err != nil {
	switch {
	case errors.Is(err, fs.ErrPermission):
		// chown/chmod the beads dir or relocate config
	case errors.Is(err, syscall.ENOSPC):
		// free disk space / raise quota, then retry
	default:
		log.Printf("config write failed: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: atomicWriteFile fails: parent dir not writable (EACCES), disk full (ENOSPC), target is inside a read-only mount or container layer, temp-file create/rename blocked by sandbox policy, or quota exceeded on the volume holding ~/.beads.

Common situations: Read-only container root filesystem with HOME on the overlay; full disk or user quota on $HOME; ~/.beads owned by root after a sudo run; SELinux/AppArmor denying writes; sync tools (Dropbox/NFS) holding locks or failing the rename.

Related errors


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