gastownhall/beads · error

open log file %q: %w

Error message

open log file %q: %w

What it means

Returned by forkExecChild when the parent cannot open (create/append to) the child's log file at opts.LogFilePath with mode 0600. The child's stdout/stderr are redirected to this file, so without it the spawn cannot proceed.

Source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:481

	if opts.Backend == BackendExternal {
		ext := opts.External
		if ext.Host != "" {
			args = append(args, "--external-host", ext.Host)
		}
		if ext.Port != 0 {
			args = append(args, "--external-port", strconv.Itoa(ext.Port))
		}
		if ext.Socket != "" {
			args = append(args, "--external-socket-path", ext.Socket)
		}
		if ext.KeepAlivePeriod != 0 {
			args = append(args, "--external-keep-alive", ext.KeepAlivePeriod.String())
		}
	}

	logFile, err := os.OpenFile(opts.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600) //nolint:gosec // G304: logFilePath is caller-derived (workspace path), not user-request input
	if err != nil {
		return nil, fmt.Errorf("open log file %q: %w", opts.LogFilePath, err)
	}

	cmd := exec.Command(self, args...)
	cmd.Stdin = nil
	cmd.Stdout = logFile
	cmd.Stderr = logFile
	cmd.SysProcAttr = procAttrDetached()

	birth, err := procid.Capture(os.Getpid())
	if err != nil {
		_ = logFile.Close()
		return nil, fmt.Errorf("capture spawning process identity: %w", err)
	}
	marker := spawnMarker{
		Schema:      1,
		PID:         os.Getpid(),
		Birth:       string(birth),
		StopEpoch:   stopEpoch,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify opts.LogFilePath's parent directory exists and is writable by the current user (mkdir -p, chown/chmod as needed).
  2. Correct the LogFilePath configuration to a valid writable location inside the workspace.
  3. Check for a read-only mount or full disk on the target path (mount, df).
  4. Clear any non-writable file occupying the log path and retry.

Example fix

// before: log path in a nonexistent dir
opts := OpenOpts{LogFilePath: "/nonexistent/dir/proxy.log"}
// after: ensure the directory exists and is writable
os.MkdirAll(filepath.Dir(logPath), 0o755)
opts := OpenOpts{LogFilePath: logPath}
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(filepath.Dir(opts.LogFilePath), 0o755); err != nil {
    return fmt.Errorf("log dir not creatable: %w", err)
}
f, err := os.OpenFile(opts.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
if err != nil { return fmt.Errorf("log path not writable: %w", err) }
f.Close()

Try / catch

ep, err := GetCreateDatabaseProxyServerEndpoint(rootDir, opts)
if err != nil && strings.Contains(err.Error(), "open log file") {
    return fmt.Errorf("check LogFilePath %q: directory must exist and be writable: %w", opts.LogFilePath, err)
}

Prevention

When it happens

Trigger: GetCreateDatabaseProxyServerEndpoint → spawnAndHandoff → forkExecChild when the log path's parent directory doesn't exist, the directory isn't writable, the path points to a read-only mount, or a file exists at that path without write permission.

Common situations: Workspace path on a read-only volume; misconfigured LogFilePath pointing outside the workspace; permissions changed by another tool; running under a different UID than the directory owner; disk full.

Related errors


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