{"record":{"id":"7b818b83bb7c8d2f","repo":"juanfont/headscale","slug":"w-got-d","errorCode":null,"errorMessage":"%w, got %d","messagePattern":"%w, got (.+?)","errorType":"validation","errorClass":"ErrBusyTimeoutNegative","httpStatus":null,"severity":"error","filePath":"hscontrol/db/sqliteconfig/config.go","lineNumber":318,"sourceCode":"}\n\n// Memory returns a configuration for in-memory databases.\nfunc Memory() *Config {\n\treturn &Config{\n\t\tPath:              \":memory:\",\n\t\tWALAutocheckpoint: -1, // not set, use driver default\n\t\tForeignKeys:       true,\n\t}\n}\n\n// Validate checks if all configuration values are valid.\nfunc (c *Config) Validate() error {\n\tif c.Path == \"\" {\n\t\treturn ErrPathEmpty\n\t}\n\n\tif c.BusyTimeout < 0 {\n\t\treturn fmt.Errorf(\"%w, got %d\", ErrBusyTimeoutNegative, c.BusyTimeout)\n\t}\n\n\tif c.JournalMode != \"\" && !c.JournalMode.IsValid() {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrInvalidJournalMode, c.JournalMode)\n\t}\n\n\tif c.AutoVacuum != \"\" && !c.AutoVacuum.IsValid() {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrInvalidAutoVacuum, c.AutoVacuum)\n\t}\n\n\tif c.WALAutocheckpoint < -1 {\n\t\treturn fmt.Errorf(\"%w, got %d\", ErrWALAutocheckpoint, c.WALAutocheckpoint)\n\t}\n\n\tif c.Synchronous != \"\" && !c.Synchronous.IsValid() {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrInvalidSynchronous, c.Synchronous)\n\t}\n","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/sqliteconfig/config.go#L300-L336","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\ncfg := sqliteconfig.Config{Path: p, BusyTimeout: -5000}\nurl, err := cfg.ToURL() // ErrBusyTimeoutNegative\n\n// after\ncfg := sqliteconfig.Config{Path: p, BusyTimeout: sqliteconfig.DefaultBusyTimeout}\nurl, err := cfg.ToURL()","handlingStrategy":"validation","validationCode":"if cfg.BusyTimeout < 0 {\n    return fmt.Errorf(\"busy_timeout must be >= 0, got %d\", cfg.BusyTimeout)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["sqlite","config","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}