thedotmack/claude-mem · warning
failed to write operator audit row
Error message
failed to write operator audit row
What it means
writeOperatorAudit inserts an audit_log row (via Postgres) recording CLI operator actions (retry/cancel) on observation-generation jobs. If the INSERT fails, it logs this warning and swallows the error — the operator action itself is not rolled back, only the audit trail entry is lost.
Solutions
- Check the logged `error` detail in the warn payload to identify the root SQL cause.
- Apply pending migrations so the audit_log table and its columns exist.
- Ensure the DB role has INSERT permission on audit_log and pgcrypto/gen_random_uuid() is available.
- Verify Postgres connectivity, then re-run the operator action if the audit row matters.
Example fix
// before: gen_random_uuid() undefined CREATE EXTENSION IF NOT EXISTS pgcrypto; // or on PG13+ -- nothing needed; verify table exists \d audit_log // after: retry the action claude-mem server jobs retry <jobId>
Defensive patterns
Strategy: validation
Validate before calling
-- Before running operator actions, verify prerequisites
SELECT to_regclass('audit_log'); -- table exists?
SELECT gen_random_uuid(); -- pgcrypto/PG13+ available?
SELECT has_table_privilege(current_user, 'audit_log', 'INSERT'); Type guard
function isAuditInsertError(e: unknown): boolean {
const msg = e instanceof Error ? e.message : String(e);
return /audit_log|permission denied|gen_random_uuid|relation .* does not exist/i.test(msg);
} Try / catch
try {
await pool.query(insertAuditSql, params);
} catch (error) {
logger.warn('SYSTEM', 'failed to write operator audit row', {
action,
jobId: job.id,
error: error instanceof Error ? error.message : String(error),
}); // deliberate swallow: audit loss must not fail the operator action
} Prevention
- Apply all DB migrations before using jobs commands
- Grant INSERT on audit_log to the CLI/server DB role
- Enable pgcrypto or use PG13+ for gen_random_uuid()
- Monitor Postgres connectivity in CI/operator environments
When it happens
Trigger: Running `jobs retry` or `jobs cancel` when the audit INSERT into audit_log fails — Postgres down, table missing, gen_random_uuid() unavailable (old PG without pgcrypto), FK violation on team_id/project_id/actor columns, or permission denied on audit_log.
Common situations: Database migration not applied (audit_log table absent); PostgreSQL < 13 without pgcrypto enabled; restricted DB role lacking INSERT on audit_log; transient connection loss.
Related errors
- agent_event_id must belong to project_id and team_id
- agent_event source_id must belong to project_id and team_id
- agent_event source_id must equal agent_event_id
- audit log insert failed
- BullMQ re-enqueue failed (will reconcile on startup)
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/78897017564cbf38.
Report an issue: GitHub.
Appendix: source
Thrown at src/npx-cli/commands/server-jobs.ts:430
interface PoolLike {
query: (sql: string, params: unknown[]) => Promise<{ rows: unknown[] }>;
}
async function writeOperatorAudit(
pool: { query: (sql: string, params: unknown[]) => Promise<unknown> },
job: JobLookup,
action: string,
details: Record<string, unknown>,
): Promise<void> {
try {
await pool.query(
`INSERT INTO audit_log (id, team_id, project_id, actor_id, api_key_id, action, resource_type, resource_id, details)
VALUES (gen_random_uuid(), $1, $2, NULL, NULL, $3, 'observation_generation_job', $4, $5::jsonb)`,
[job.team_id, job.project_id, action, job.id, JSON.stringify({ ...details, source: 'cli_operator' })],
);
} catch (error) {
logger.warn('SYSTEM', 'failed to write operator audit row', {
action,
jobId: job.id,
error: error instanceof Error ? error.message : String(error),
});
}
}
function extractRetriedCount(payload: Record<string, unknown> | null | undefined): number {
if (!payload || typeof payload !== 'object') return 0;
const value = (payload as { retried_count?: unknown }).retried_count;
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0;
}
function detectDivergence(
pg: Record<string, number>,
bullmq: Record<string, { waiting: number; active: number; completed: number; failed: number; delayed: number; stalled: number }> | null,
): Record<string, unknown> {
if (!bullmq) return { reason: 'bullmq_unavailable' };View on GitHub (pinned to d8bc9755e7)