{"record":{"id":"4c944a5f1e0db8c9","repo":"juanfont/headscale","slug":"invalid-config-w","errorCode":null,"errorMessage":"invalid config: %w","messagePattern":"invalid config: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/db/sqliteconfig/config.go","lineNumber":349,"sourceCode":"\t}\n\n\tif c.Synchronous != \"\" && !c.Synchronous.IsValid() {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrInvalidSynchronous, c.Synchronous)\n\t}\n\n\tif c.TxLock != \"\" && !c.TxLock.IsValid() {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrInvalidTxLock, c.TxLock)\n\t}\n\n\treturn nil\n}\n\n// ToURL builds a properly encoded SQLite connection string using _pragma parameters\n// compatible with modernc.org/sqlite driver.\nfunc (c *Config) ToURL() (string, error) {\n\terr := c.Validate()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid config: %w\", err)\n\t}\n\n\t// Handle different database types\n\tvar baseURL string\n\tif c.Path == \":memory:\" {\n\t\tbaseURL = \":memory:\"\n\t} else {\n\t\tbaseURL = \"file:\" + c.Path\n\t}\n\n\t// Build query parameters\n\tvar queryParts []string\n\n\t// Add _txlock first (it's a connection parameter, not a pragma)\n\tif c.TxLock != \"\" {\n\t\tqueryParts = append(queryParts, \"_txlock=\"+string(c.TxLock))\n\t}\n","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/sqliteconfig/config.go#L331-L367","documentation":"ToURL() calls Validate() first and wraps any validation failure with the 'invalid config: ' prefix before building the modernc.org/sqlite connection string. This error is a wrapper: the real cause is one of the sentinel validation errors (empty path, negative busy_timeout, invalid journal_mode/auto_vacuum/wal_autocheckpoint/synchronous/txlock).","triggerScenarios":"Calling sqliteconfig.Config.ToURL() on any config that fails Validate(): empty Path, negative BusyTimeout, or any invalid enum pragma value. ToURL is the last step before opening the database, so this surfaces at headscale startup.","commonSituations":"Malformed database section in headscale's config file (typo'd pragma names, negative numbers), or programmatic configs built with unset/zero fields (empty Path triggers ErrPathEmpty).","solutions":["Unwrap the error chain (errors.Is against the sqliteconfig.Err* sentinels) to find the exact field at fault.","Fix the offending field per its sentinel's contract (non-empty path, valid pragma strings, proper ranges).","Call cfg.Validate() early at config-load time to get a precise error before ToURL is reached."],"exampleFix":"// before\nurl, err := cfg.ToURL()\nif err != nil { log.Fatal(err) } // 'invalid config: invalid journal_mode: wal'\n\n// after\nif err := cfg.Validate(); err != nil { // precise, actionable\n    log.Fatal().Err(err).Msg(\"sqlite config\")\n}\nurl, err := cfg.ToURL()","handlingStrategy":"try-catch","validationCode":"// Fail fast with a precise error before building the DSN:\nif err := cfg.Validate(); err != nil {\n    return fmt.Errorf(\"validating sqlite config: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"url, err := cfg.ToURL()\nif err != nil {\n    switch {\n    case errors.Is(err, sqliteconfig.ErrPathEmpty),\n        errors.Is(err, sqliteconfig.ErrBusyTimeoutNegative),\n        errors.Is(err, sqliteconfig.ErrInvalidJournalMode),\n        errors.Is(err, sqliteconfig.ErrInvalidAutoVacuum),\n        errors.Is(err, sqliteconfig.ErrWALAutocheckpoint),\n        errors.Is(err, sqliteconfig.ErrInvalidSynchronous),\n        errors.Is(err, sqliteconfig.ErrInvalidTxLock):\n        // config bug: report field-level error, do not retry\n    }\n    return err\n}","preventionTips":["Run cfg.Validate() during config parsing so errors point at the exact field.","Build configs from the package's typed constants instead of raw strings.","Fail headscale startup on config validation errors rather than falling back to defaults."],"tags":["sqlite","config","validation","connection-string"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}