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

  1. Ensure identity.WriteSecret was called for this rootDir before ReadSecret (correct startup order)
  2. Verify the file exists: stat <rootDir>/proxy.secret, and confirm rootDir is the right workspace
  3. Fix read permissions (chown/chmod) — the file is 0600 and only the owner can read it
  4. 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

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


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