juanfont/headscale · error · ErrBusyTimeoutNegative
%w, got %d
Error message
%w, got %d
What it means
Config.Validate rejected the SQLite configuration because BusyTimeout is negative. The sqliteconfig package requires busy_timeout >= 0 milliseconds; this pragma controls how long SQLite waits on a locked database before returning SQLITE_BUSY. The sentinel ErrBusyTimeoutNegative is wrapped with the offending value.
Source
Thrown at hscontrol/db/sqliteconfig/config.go:318
}
// Memory returns a configuration for in-memory databases.
func Memory() *Config {
return &Config{
Path: ":memory:",
WALAutocheckpoint: -1, // not set, use driver default
ForeignKeys: true,
}
}
// Validate checks if all configuration values are valid.
func (c *Config) Validate() error {
if c.Path == "" {
return ErrPathEmpty
}
if c.BusyTimeout < 0 {
return fmt.Errorf("%w, got %d", ErrBusyTimeoutNegative, c.BusyTimeout)
}
if c.JournalMode != "" && !c.JournalMode.IsValid() {
return fmt.Errorf("%w: %s", ErrInvalidJournalMode, c.JournalMode)
}
if c.AutoVacuum != "" && !c.AutoVacuum.IsValid() {
return fmt.Errorf("%w: %s", ErrInvalidAutoVacuum, c.AutoVacuum)
}
if c.WALAutocheckpoint < -1 {
return fmt.Errorf("%w, got %d", ErrWALAutocheckpoint, c.WALAutocheckpoint)
}
if c.Synchronous != "" && !c.Synchronous.IsValid() {
return fmt.Errorf("%w: %s", ErrInvalidSynchronous, c.Synchronous)
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Set BusyTimeout to a non-negative millisecond value (DefaultBusyTimeout = 10000 is a safe choice).
- If the value comes from a config file, check for a stray minus sign or a bad unit conversion.
- Remember: -1 is valid for WALAutocheckpoint but NOT for BusyTimeout.
Example fix
// before
cfg := sqliteconfig.Config{Path: p, BusyTimeout: -5000}
url, err := cfg.ToURL() // ErrBusyTimeoutNegative
// after
cfg := sqliteconfig.Config{Path: p, BusyTimeout: sqliteconfig.DefaultBusyTimeout}
url, err := cfg.ToURL() Defensive patterns
Strategy: validation
Validate before calling
if cfg.BusyTimeout < 0 {
return fmt.Errorf("busy_timeout must be >= 0, got %d", cfg.BusyTimeout)
} Prevention
- Default to sqliteconfig.DefaultBusyTimeout (10000 ms) instead of hand-picking values.
- Validate config at load time, not at ToURL time.
- Remember only wal_autocheckpoint accepts -1 as a sentinel.
When it happens
Trigger: Constructing a sqliteconfig.Config with a negative BusyTimeout (e.g. from parsing a config file where the value was set to -1 or computed as a negative duration), then calling Validate() or ToURL().
Common situations: Configuration typos (negative number), or code translating time.Duration in nanoseconds to milliseconds incorrectly producing a negative value; copying a wal_autocheckpoint=-1 convention onto busy_timeout.
Related errors
- path cannot be empty
- busy_timeout must be >= 0
- %w: %s
- invalid config: %w
- wildcard (*) is not supported as SSH destination
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/7b818b83bb7c8d2f.
Report an issue: GitHub.