{"record":{"id":"ecc96f0db9d10c1f","repo":"gastownhall/beads","slug":"journal-record-s-for-s-w","errorCode":null,"errorMessage":"journal: record %s for %s: %w","messagePattern":"journal: record (.+?) for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/journal.go","lineNumber":466,"sourceCode":"\t\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?)\n\t\t`, seq, time.Now().UTC(), string(op), issueID, actor, issueJSON, depJSON, commentJSON)\n\t\treturn err\n\t}\n\n\tseq, err := nextEventSeq(ctx, tx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := insert(seq); err != nil {\n\t\t// A duplicate seq means the counter is BEHIND the journal — it was\n\t\t// restored, hand-edited, or copied from another workspace. Left alone\n\t\t// that wedges the instance permanently: every later mutation re-mints\n\t\t// the same taken seq and fails, and because the journal row shares the\n\t\t// mutation's transaction, the user's write fails with it. Raise the\n\t\t// counter past the high-water mark and retry exactly once; a second\n\t\t// duplicate is a real bug and must surface, not spin.\n\t\tif !dberrors.IsDuplicateKey(err) {\n\t\t\treturn fmt.Errorf(\"journal: record %s for %s: %w\", op, issueID, err)\n\t\t}\n\t\tif healErr := healEventSeqCounter(ctx, tx); healErr != nil {\n\t\t\treturn healErr\n\t\t}\n\t\tseq, err = nextEventSeq(ctx, tx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := insert(seq); err != nil {\n\t\t\treturn fmt.Errorf(\"journal: record %s for %s after seq counter heal: %w\", op, issueID, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// healEventSeqCounter seeds the counter row if it is missing and raises it to\n// the journal's high-water mark, so the next allocation cannot collide. VALUES +\n// GREATEST, not INSERT ... SELECT MAX(): in Dolt a literal+aggregate SELECT over","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/journal.go#L448-L484","documentation":"The journal INSERT failed with a non-duplicate-key error; insertEventRow wraps it as 'journal: record <op> for <id>'. Duplicate-key errors are specially handled by healing the seq counter, so anything reaching this wrap is a genuine SQL failure (constraint violation, connection loss, permissions, table missing).","triggerScenarios":"INSERT INTO bd_events_journal fails for reasons other than duplicate seq: connection dropped mid-transaction, bd_events_journal missing, CHECK/FK constraint violated, disk full, Dolt driver error.","commonSituations":"Schema not migrated (journal table absent); database read-only or permission revoked; Dolt server restarted mid-write; disk quota exceeded on the host.","solutions":["Read the wrapped %w error for the SQL root cause","Run bd doctor / migrations to ensure bd_events_journal exists","Check DB permissions and disk space","Retry the operation if the cause was transient (connection drop)","If it persists after a seq-heal retry, it indicates real corruption — restore from backup"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure journal schema exists before writes\nif err := migrate.Run(ctx, db); err != nil { return err }\n_ = db.QueryRow(\"SELECT 1 FROM bd_events_journal LIMIT 1\")","typeGuard":null,"tryCatchPattern":"err := ops.RecordEventInTx(ctx, tx, op, id, nil, nil)\nif err != nil {\n    if !errors.Is(err, storage.ErrNotFound) && isTransientSQL(err) {\n        return retryWithBackoff(func() error { return doWrite(ctx, tx2, op, id) })\n    }\n    return err\n}","preventionTips":["Keep schema migrated (bd_events_journal present)","Monitor disk space and DB permissions","Use connection retry policy for Dolt/driver drops","Classify errors: only non-duplicate, transient errors warrant retry"],"tags":["journal","database","sql-insert"],"backgroundTag":"journal-seq-insert-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}