{"record":{"id":"91a6412f87348d9e","repo":"OpenNHP/opennhp","slug":"keystore-open-database-s-w","errorCode":null,"errorMessage":"keystore: open database %s: %w","messagePattern":"keystore: open database (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/keystore.go","lineNumber":42,"sourceCode":"// public key when the operator has not configured agentKeyTTLSeconds.\n// 24 hours. Mirrors how OTPTTLSeconds is defaulted at the helper layer.\nconst DefaultAgentKeyTTLSeconds int64 = 86400\n\n// NewAgentKeyStore opens (or creates) the SQLite database at dbPath.\n// The directory is created if it does not exist.\nfunc NewAgentKeyStore(dbPath string) (*AgentKeyStore, error) {\n\tif dbPath == \"\" {\n\t\tdbPath = filepath.Join(\"data\", \"nhp_server.db\")\n\t}\n\n\tdir := filepath.Dir(dbPath)\n\tif err := os.MkdirAll(dir, 0700); err != nil {\n\t\treturn nil, fmt.Errorf(\"keystore: create directory %s: %w\", dir, err)\n\t}\n\n\tdb, err := sql.Open(\"sqlite\", dbPath+\"?_journal_mode=WAL&_busy_timeout=5000\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"keystore: open database %s: %w\", dbPath, err)\n\t}\n\n\t// Connection pool tuning — SQLite is single-writer; one open conn is\n\t// usually correct. Keep a small idle pool for concurrent read queries.\n\tdb.SetMaxOpenConns(1)\n\tdb.SetMaxIdleConns(1)\n\tdb.SetConnMaxLifetime(0)\n\n\tstore := &AgentKeyStore{db: db}\n\tif err := store.migrate(); err != nil {\n\t\tdb.Close()\n\t\treturn nil, fmt.Errorf(\"keystore: migrate: %w\", err)\n\t}\n\n\tlog.Info(\"keystore: database opened at %s\", dbPath)\n\treturn store, nil\n}\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/keystore.go#L24-L60","documentation":"NewAgentKeyStore wraps any error from sql.Open (driver registration / DSN parse) with this message. sql.Open for the 'sqlite' driver rarely touches disk — it fails mainly when the driver is not registered (blank import missing) or the DSN is malformed. The wrapped cause identifies which.","triggerScenarios":"Calling NewAgentKeyStore with a driver name mismatch (e.g. modernc.org/sqlite vs mattn/go-sqlite3 DSN style), or when the sqlite driver was never imported so sql.Open returns 'sql: unknown driver'.","commonSituations":"Adding a dependency swap without updating the DSN query parameters (e.g. _journal_mode=WAL unsupported by the chosen driver), or building with a driver package accidentally dropped from go.mod.","solutions":["Verify the sqlite driver is imported for its side effect: _ \"modernc.org/sqlite\" (or the mattn variant).","Check the DSN query params match the driver: _journal_mode=WAL&_busy_timeout=5000 works for modernc/mattn; other drivers differ.","Confirm go.mod contains the sqlite driver module and the binary was rebuilt.","Log the wrapped %w cause — it names the driver/DSN problem exactly."],"exampleFix":"// before\nimport (\n    \"database/sql\"\n)\n// after\nimport (\n    \"database/sql\"\n    _ \"modernc.org/sqlite\"\n)","handlingStrategy":"validation","validationCode":"// driver must be registered before opening\nimport _ \"modernc.org/sqlite\"\nif _, err := sql.Open(\"sqlite\", \":memory:\"); err != nil {\n    return fmt.Errorf(\"sqlite driver unavailable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Blank-import the sqlite driver in package main/init","Keep DSN query params matched to the chosen driver","Pin the sqlite driver version in go.mod"],"tags":["go","sqlite","keystore","driver"],"backgroundTag":"file-open-failed","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}