gastownhall/beads · error
identity: read proxy secret: %w
Error message
identity: read proxy secret: %w
What it means
ReadSecret loads <rootDir>/proxy.secret and validates it is a 64-character hex string. This error wraps any failure from os.ReadFile: the secret file does not exist, permission is denied, or it is a directory. The library throws it because the control-listener secret cannot be read back for authentication; note the intentionally-rotated secret means a stale reader will simply not find the current file.
Source
Thrown at internal/storage/dbproxy/identity/identity.go:58
// 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, nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Ensure identity.WriteSecret was called for this rootDir before ReadSecret (correct startup order)
- Verify the file exists: stat <rootDir>/proxy.secret, and confirm rootDir is the right workspace
- Fix read permissions (chown/chmod) — the file is 0600 and only the owner can read it
- Handle fs.ErrNotExist explicitly with errors.Is and fall back to writing a fresh secret
Example fix
// before
secret, err := identity.ReadSecret(rootDir) // fails if proxy.secret missing
// after
secret, err := identity.ReadSecret(rootDir)
if errors.Is(err, fs.ErrNotExist) {
secret, err = identity.WriteSecret(rootDir)
} Defensive patterns
Strategy: validation
Validate before calling
secretPath := filepath.Join(rootDir, identity.SecretFileName)
if _, err := os.Stat(secretPath); err != nil {
if errors.Is(err, fs.ErrNotExist) {
// write a fresh secret before reading
_, err = identity.WriteSecret(rootDir)
return err
}
return err
} Try / catch
secret, err := identity.ReadSecret(rootDir)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrNotExist) {
// proxy never started here or workspace reset: recreate via WriteSecret
} else if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrPermission) {
// wrong user: chown or re-run as the proxy's user
}
return err
} Prevention
- Establish startup order: proxy writes the secret before clients/children read it
- Run all workspace processes as the same user (file mode is 0600)
- Detect stale workspaces: recreate proxy.secret after workspace recreation
- Point every process at the same rootDir value
When it happens
Trigger: Calling identity.ReadSecret(rootDir) before WriteSecret ever ran (proxy.secret absent), after the workspace root was recreated or moved, when the file exists but the current user lacks read permission (0600 owned by another user), or when rootDir points at the wrong workspace.
Common situations: A client or child process starting before the parent proxy wrote the secret (startup race), running as a different user than the proxy (0600 perms deny access), pointing at a stale or deleted workspace, or a workspace partially restored from backup without proxy.secret.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- read %s: %w
- dolt path is not executable
- server: NewDoltServer: failed to determine absolute path of
- server: NewDoltServer: failed to determine absolute path of
- server: NewDoltServer: failed to determine absolute path of
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/73fa2b32065df91c.
Report an issue: GitHub.