{"record":{"id":"02ba72d7be063233","repo":"gastownhall/beads","slug":"journal-seed-seq-counter-w","errorCode":null,"errorMessage":"journal: seed seq counter: %w","messagePattern":"journal: seed seq counter: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/journal.go","lineNumber":490,"sourceCode":"\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\n// an empty table yields zero rows, so an INSERT ... SELECT would seed nothing on\n// a fresh journal. GREATEST also makes this safe to call on a counter that is\n// already ahead — it never moves the counter backwards.\nfunc healEventSeqCounter(ctx context.Context, tx DBTX) error {\n\tif _, err := tx.ExecContext(ctx, \"INSERT IGNORE INTO bd_events_seq (id, next_seq) VALUES (0, 0)\"); err != nil {\n\t\treturn fmt.Errorf(\"journal: seed seq counter: %w\", err)\n\t}\n\tif _, err := tx.ExecContext(ctx, `\n\t\tUPDATE bd_events_seq\n\t\tSET next_seq = GREATEST(next_seq, COALESCE((SELECT MAX(seq) FROM bd_events_journal), 0))\n\t\tWHERE id = 0\n\t`); err != nil {\n\t\treturn fmt.Errorf(\"journal: raise seq counter to high-water mark: %w\", err)\n\t}\n\treturn nil\n}\n\n// nextEventSeq allocates the next journal sequence number from the single-row\n// bd_events_seq counter, INSIDE the caller's transaction. Incrementing the\n// shared counter row is what serializes seq assignment: two transactions that\n// both allocate a seq contend on the one row, so only one commit order survives.\n// The value becomes the journal row's seq, yielding gapless, commit-ordered seqs\n// (a rolled-back transaction rolls back its increment, burning no seq). The\n// counter persists across restart and prune never touches it, so seq never","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/journal.go#L472-L508","documentation":"healEventSeqCounter seeds the singleton bd_events_seq row with INSERT IGNORE; if that exec fails the error is wrapped as 'journal: seed seq counter'. Seeding is part of seq-counter healing and of normal nextEventSeq allocation, so any journal write can surface this when the seq table is missing or the driver rejects the statement.","triggerScenarios":"bd_events_seq table missing (schema not migrated), INSERT IGNORE unsupported/failed by driver, connection error, or insufficient INSERT privileges on that table.","commonSituations":"Running a new beads version against an old database without migrations; read-only replicas receiving writes; Dolt version that mishandles INSERT IGNORE.","solutions":["Run schema migrations / bd doctor to create bd_events_seq","Check INSERT privileges on bd_events_seq","Verify DB connectivity and retry","Confirm the Dolt driver version supports INSERT IGNORE"],"exampleFix":"// before\nops.RecordEventInTx(ctx, tx, op, id, nil, nil) // fails: bd_events_seq missing\n// after\nif err := migrate.Run(ctx, db); err != nil { return err } // creates bd_events_seq\nops.RecordEventInTx(ctx, tx, op, id, nil, nil)","handlingStrategy":"validation","validationCode":"_, err := db.Exec(\"SELECT 1 FROM bd_events_seq LIMIT 1\")\nif err != nil { return fmt.Errorf(\"bd_events_seq missing; run migrations: %w\", err) }","typeGuard":null,"tryCatchPattern":"if err := doJournalWrite(ctx, tx); err != nil {\n    if strings.Contains(err.Error(), \"seed seq counter\") {\n        return fmt.Errorf(\"schema/permission issue on bd_events_seq: %w\", err)\n    }\n    return err\n}","preventionTips":["Run migrations whenever beads version changes","Grant INSERT/UPDATE on bd_events_seq to the app user","Verify driver supports INSERT IGNORE (MySQL/Dolt dialects)"],"tags":["journal","database","sequence","schema"],"backgroundTag":"journal-seq-counter-unhealthy","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}