{"record":{"id":"b21f89149427ab6d","repo":"OpenNHP/opennhp","slug":"keystore-insert-agent-key-w","errorCode":null,"errorMessage":"keystore: insert agent key: %w","messagePattern":"keystore: insert agent key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/keystore.go","lineNumber":416,"sourceCode":"\t\texpiresAt = sql.NullInt64{Int64: now + ttlSeconds, Valid: true}\n\t}\n\n\t// Upsert: insert or update on (usr_id, dev_id) conflict. Both fresh\n\t// inserts and key rotations (including cipher scheme switches) reset\n\t// the clock.\n\t_, err = s.db.Exec(\n\t\t`INSERT INTO agent_keys (usr_id, dev_id, public_key, cipher, created_at, expires_at, active)\n\t\t VALUES (?, ?, ?, ?, ?, ?, 1)\n\t\t ON CONFLICT(usr_id, dev_id) DO UPDATE SET\n\t\t   public_key = excluded.public_key,\n\t\t   cipher     = excluded.cipher,\n\t\t   created_at = excluded.created_at,\n\t\t   expires_at = excluded.expires_at,\n\t\t   active     = 1`,\n\t\tuserId, deviceId, pubKey, cipherScheme, now, expiresAt,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"keystore: insert agent key: %w\", err)\n\t}\n\n\tlog.Info(\"keystore: agent key registered for user=%s device=%s cipher=%d ttl=%ds\", userId, deviceId, cipherScheme, ttlSeconds)\n\treturn nil\n}\n\n// GetAgentKey returns the public key for a given user+device, or nil if\n// not found OR if the row is past its expires_at. Expired rows are\n// indistinguishable from never-registered ones to all callers.\nfunc (s *AgentKeyStore) GetAgentKey(userId, deviceId string) (*AgentKeyRecord, error) {\n\trec := &AgentKeyRecord{}\n\tvar expiresAt sql.NullInt64\n\tvar active int\n\terr := s.db.QueryRow(\n\t\t`SELECT usr_id, dev_id, public_key, cipher, created_at, expires_at, active\n\t\t FROM agent_keys\n\t\t WHERE usr_id = ? AND dev_id = ? AND active = 1\n\t\t   AND (expires_at IS NULL OR expires_at > ?)`,","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/keystore.go#L398-L434","documentation":"RegisterAgentKey performs an INSERT ... ON CONFLICT DO UPDATE (upsert) into agent_keys; failure is wrapped as 'keystore: insert agent key'. Causes include constraint violations, SQLITE_BUSY, disk I/O errors, or a driver not supporting the upsert syntax.","triggerScenarios":"Upsert Exec fails: UNIQUE constraint conflict beyond the handled idempotent path, WAL checkpoint I/O failure on a full disk, or old SQLite versions (<3.24) lacking ON CONFLICT DO UPDATE support.","commonSituations":"Embedded/older SQLite builds in the driver rejecting the upsert syntax ('near ON: syntax error'), disk-full containers, concurrent writers across processes.","solutions":["Check the wrapped error: 'syntax error near ON' means the driver's SQLite is too old — upgrade modernc.org/sqlite or mattn/go-sqlite3.","Free disk space / verify volume writability (WAL needs db, -wal, -shm writes).","Reduce cross-process write contention or serialize registrations.","Re-run the registration; the upsert is idempotent for the same key."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := store.RegisterAgentKey(u, d, pk, cs, ttl)\nif err != nil && strings.Contains(err.Error(), \"insert agent key\") {\n    if strings.Contains(err.Error(), \"syntax error\") {\n        log.Fatal(\"sqlite driver too old for ON CONFLICT upsert; upgrade driver\")\n    }\n    // bounded retry for transient BUSY\n}","preventionTips":["Use a driver bundling SQLite >= 3.24 for upsert support","Monitor disk space for WAL checkpoints","Keep a single writer process per keystore file"],"tags":["go","sqlite","upsert","registration"],"backgroundTag":"database-write-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"}