gastownhall/beads · error
identity: write proxy secret: %w
Error message
identity: write proxy secret: %w
What it means
WriteSecret writes the generated hex secret to <rootDir>/proxy.secret using atomicfile.WriteFile with 0600 permissions. This error wraps any failure from that atomic write: inability to create the root directory path, permission denied, read-only filesystem, disk full, or a failure in the atomic rename/replace step. The library throws it because the control-listener secret could not be persisted for later readers.
Source
Thrown at internal/storage/dbproxy/identity/identity.go:49
}
resolved, err := filepath.EvalSymlinks(abs)
if err != nil {
return "", fmt.Errorf("identity: resolve root path: %w", err)
}
sum := sha256.Sum256([]byte(resolved))
return hex.EncodeToString(sum[:]), nil
}
// WriteSecret creates and atomically writes a new control-listener secret.
// Each proxy start intentionally rotates the previous secret.
func WriteSecret(rootDir string) (string, error) {
raw := make([]byte, 32)
if _, err := rand.Read(raw); err != nil {
return "", fmt.Errorf("identity: generate proxy secret: %w", err)
}
secret := hex.EncodeToString(raw)
if err := atomicfile.WriteFile(filepath.Join(rootDir, SecretFileName), []byte(secret+"\n"), 0o600); err != nil {
return "", fmt.Errorf("identity: write proxy secret: %w", err)
}
return secret, nil
}
// ReadSecret reads and validates the control-listener secret.
func ReadSecret(rootDir string) (string, error) {
data, err := os.ReadFile(filepath.Join(rootDir, SecretFileName)) // #nosec G304 - rootDir is the workspace proxy root, not user input
if err != nil {
return "", fmt.Errorf("identity: read proxy secret: %w", err)
}
secret := strings.TrimSpace(string(data))
if len(secret) != 64 {
return "", errors.New("identity: invalid proxy secret")
}
if _, err := hex.DecodeString(secret); err != nil {
return "", errors.New("identity: invalid proxy secret")
}
return secret, nilView on GitHub (pinned to 71377f2769)
Solutions
- Ensure rootDir exists and is writable: os.MkdirAll(rootDir, 0o755) and check ownership
- Check disk space (df) and that the filesystem is mounted read-write
- Fix file/directory permissions on <rootDir>/proxy.secret (chown/chmod) to match the running user
- Read the wrapped error (errors.Unwrap) for the exact OS cause (fs.ErrNotExist, fs.ErrPermission, etc.)
Example fix
// before
secret, err := identity.WriteSecret(rootDir) // fails if rootDir missing
// after
if err := os.MkdirAll(rootDir, 0o755); err != nil {
return err
}
secret, err := identity.WriteSecret(rootDir) Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(rootDir)
if err != nil {
return fmt.Errorf("root %q missing: %w", rootDir, err)
}
if !info.IsDir() {
return fmt.Errorf("%q is not a directory", rootDir)
}
if err := unix.Access(rootDir, unix.W_OK); err != nil {
return fmt.Errorf("root %q not writable: %w", rootDir, err)
} Try / catch
secret, err := identity.WriteSecret(rootDir)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrPermission) {
// fix ownership/permissions on rootDir or proxy.secret
}
return err
} Prevention
- Always create rootDir with os.MkdirAll before WriteSecret
- Run all bd processes for a workspace as the same user (secret file is 0600)
- Check disk space and read-only mounts in deployment health checks
- Ensure temp-file creation in rootDir is allowed (atomic rename needs a writable directory)
When it happens
Trigger: Calling identity.WriteSecret(rootDir) when rootDir does not exist or is not writable, when proxy.secret exists but is not writable/replaceable by the current user, when the filesystem is read-only or full, or when atomicfile's temp-file + rename sequence fails (e.g. cross-device issue or locked directory).
Common situations: Running bd as a different user than the one who owns the workspace (stale permissions), running with a read-only mount or full disk, pointing at a rootDir path that was never created, or SELinux/AppArmor blocking writes to the directory.
Related errors
- failed to create temp file: %w
- ensureProxiedServerConfig: write %s: %w
- dolt path is not executable
- failed to create backup directory: %w
- failed to close temp file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/490e1e910b56ce86.
Report an issue: GitHub.