OpenNHP/opennhp · error
keystore: migrate
Error message
keystore: migrate: %w
What it means
NewAgentKeyStore fails when store.migrate() returns an error; it closes the DB and wraps the cause as 'keystore: migrate'. This is a coarse wrapper — the real reason (DDL failure, locked DB, schema conflict) is inside the wrapped error chain. Inspect errors.Unwrap / %v of the chain.
Solutions
- Inspect the wrapped cause: fmt.Sprintf("%+v", err) or errors.Unwrap to find the inner migrate/SQL error.
- Check directory and db file writability (needs 0700 dir, write access to db, -wal, -shm files).
- Ensure only one process owns the keystore at a time; the WAL busy_timeout is 5000ms and can be exceeded under contention.
- If the file is corrupt, back up and remove keystore.db (data loss) or use sqlite3 .recover.
Example fix
// before
store, err := NewAgentKeyStore(dir)
if err != nil { return err }
// after
store, err := NewAgentKeyStore(dir)
if err != nil {
log.Error("keystore init failed: %v", err) // includes wrapped migrate cause
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure dir is writable before init
if info, err := os.Stat(dir); err != nil || info.Mode().Perm()&0700 == 0 {
os.MkdirAll(dir, 0700)
} Try / catch
store, err := NewAgentKeyStore(dir)
if err != nil {
var inner error
for e := err; e != nil; e = errors.Unwrap(e) { inner = e }
log.Error("keystore init failed, root cause: %v", inner)
return err
} Prevention
- Run one daemon per keystore file
- Keep keystore on local disk, never NFS
- Monitor free disk space
- Log the full error chain, not just the top wrapper
When it happens
Trigger: First run against a corrupt or non-writable SQLite file, concurrent processes migrating simultaneously, or a migrate DDL statement failing (see errors 132/133 for the inner wrappers).
Common situations: Deploying two daemon instances pointed at the same keystore.db file, a read-only filesystem or directory permissions blocking WAL creation, or leftover -wal/-shm files from a crashed process.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- keystore: open database
- keystore: check column
- keystore: migrate .
- keystore: get agent key
- keystore: insert otp
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/88ef94567ff91420.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/keystore.go:54
if err := os.MkdirAll(dir, 0700); err != nil {
return nil, fmt.Errorf("keystore: create directory %s: %w", dir, err)
}
db, err := sql.Open("sqlite", dbPath+"?_journal_mode=WAL&_busy_timeout=5000")
if err != nil {
return nil, fmt.Errorf("keystore: open database %s: %w", dbPath, err)
}
// Connection pool tuning — SQLite is single-writer; one open conn is
// usually correct. Keep a small idle pool for concurrent read queries.
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
db.SetConnMaxLifetime(0)
store := &AgentKeyStore{db: db}
if err := store.migrate(); err != nil {
db.Close()
return nil, fmt.Errorf("keystore: migrate: %w", err)
}
log.Info("keystore: database opened at %s", dbPath)
return store, nil
}
// Close closes the database connection.
func (s *AgentKeyStore) Close() error {
return s.db.Close()
}
// migrate creates tables if they do not exist and applies incremental schema
// changes to existing databases.
func (s *AgentKeyStore) migrate() error {
ddl := `
CREATE TABLE IF NOT EXISTS otp_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
usr_id TEXT NOT NULL,View on GitHub (pinned to 6e04ca5ff0)