gastownhall/beads · info
errIdleTimeout
errIdleTimeout
Error message
idle timeout reached
What it means
errIdleTimeout is returned by the proxy's idleWatcher when no client activity occurs for the configured idle timeout. The proxy then shuts itself down (including the backend) as a resource-saving measure. It is an internal lifecycle sentinel, not an operational fault.
Source
Thrown at internal/storage/dbproxy/proxy/server.go:92
const LockHeldExitCode = 75
// ErrLockHeld is returned from ListenAndServe when another proxy already
// holds proxy.lock for the same rootDir. It is a normal "lost the race"
// outcome, not a failure: callers spawned as children should map it to
// LockHeldExitCode and exit cleanly.
var ErrLockHeld = errors.New("proxy lock held by another proxy on this rootDir")
const (
serverReadyTimeout = 30 * time.Second
readyDialTimeout = 2 * time.Second
readyInitialBackoff = 50 * time.Millisecond
readyMaxBackoff = 1 * time.Second
idleWatcherMinInterval = 1 * time.Second
backendStopTimeout = 5 * time.Minute
tcpKeepAlivePeriod = 30 * time.Second
)
var errIdleTimeout = errors.New("idle timeout reached")
func NewProxyServer(opts ProxyOpts) *proxyServer {
return &proxyServer{
rootDir: opts.RootDir,
port: opts.Port,
idleTimeout: opts.IdleTimeout,
server: opts.Server,
stats: opts.Stats,
stopEpoch: opts.StopEpoch,
}
}
func (p *proxyServer) tracef(format string, args ...any) {
p.logger.Printf(format, args...)
}
func (p *proxyServer) ListenAndServe(parentCtx context.Context) error {
lock, err := util.TryLock(filepath.Join(p.rootDir, LockFileName))View on GitHub (pinned to 71377f2769)
Solutions
- Increase IdleTimeout in ProxyOpts to cover expected quiet periods
- Set a long/very large idle timeout for daemons that must stay up
- Reconnect on demand: the proxy is designed to restart when a client needs it
Example fix
// before
srv := proxy.NewProxyServer(proxy.ProxyOpts{RootDir: root, IdleTimeout: 30 * time.Second})
// after
srv := proxy.NewProxyServer(proxy.ProxyOpts{RootDir: root, IdleTimeout: 30 * time.Minute}) Defensive patterns
Strategy: try-catch
Validate before calling
if opts.IdleTimeout != 0 && opts.IdleTimeout < time.Minute {
opts.IdleTimeout = time.Minute // floor for interactive workloads
} Try / catch
if err := srv.ListenAndServe(ctx); err != nil {
if errors.Is(err, errIdleTimeout) || strings.Contains(err.Error(), "idle timeout") {
return nil // expected self-shutdown; clients will restart on demand
}
return err
} Prevention
- Size IdleTimeout to your longest expected quiet period
- Treat idle shutdown as normal lifecycle, not failure
- Keep clients resilient: reconnect transparently when the proxy is down
When it happens
Trigger: Running ListenAndServe with a short ProxyOpts.IdleTimeout and letting no clients connect/dial for that duration; long-running sessions with a quiet period exceeding the timeout.
Common situations: Long think-time pauses in agent workflows; background daemon stopped unexpectedly by the idle watcher; misconfigured IdleTimeout (e.g. seconds instead of minutes).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- server: ExternalDoltServer.Start: server already started
- errClosed
- server started (PID %d) but not accepting connections on por
- server started (PID %d) but not accepting connections on por
- timeout after %s waiting for server at %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/47f900151246cf5d.
Report an issue: GitHub.