{"record":{"id":"293842b89b2e7ed6","repo":"gastownhall/beads","slug":"failed-to-insert-initial-issue-counter-for-prefix","errorCode":null,"errorMessage":"failed to insert initial issue counter for prefix %q: %w","messagePattern":"failed to insert initial issue counter for prefix %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/issues.go","lineNumber":888,"sourceCode":"\t\t// No counter row yet - seed from existing issues before proceeding to\n\t\t// avoid collisions with manually-created sequential IDs (GH#2002).\n\t\tif seedErr := seedCounterFromExistingIssuesTx(ctx, tx, prefix); seedErr != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to seed issue counter for prefix %q: %w\", prefix, seedErr)\n\t\t}\n\t\t// Retry the atomic increment after seeding.\n\t\tres, err = tx.ExecContext(ctx, \"UPDATE issue_counter SET last_id = last_id + 1 WHERE prefix = ?\", prefix)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to increment issue counter after seeding for prefix %q: %w\", prefix, err)\n\t\t}\n\t\trowsAffected, err = res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to check rows affected after seeding for prefix %q: %w\", prefix, err)\n\t\t}\n\t\tif rowsAffected == 0 {\n\t\t\t// Seeding found no existing numeric IDs -- insert the initial row.\n\t\t\t_, err = tx.ExecContext(ctx, \"INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)\", prefix)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to insert initial issue counter for prefix %q: %w\", prefix, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Read back the value that was atomically set by the DB engine.\n\tvar nextID int\n\terr = tx.QueryRowContext(ctx, \"SELECT last_id FROM issue_counter WHERE prefix = ?\", prefix).Scan(&nextID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read issue counter after increment for prefix %q: %w\", prefix, err)\n\t}\n\treturn fmt.Sprintf(\"%s-%d\", prefix, nextID), nil\n}\n\n// isCounterModeTx checks whether issue_id_mode=counter is configured.\nfunc isCounterModeTx(ctx context.Context, tx *sql.Tx) (bool, error) {\n\tvar idMode string\n\terr := tx.QueryRowContext(ctx, \"SELECT value FROM config WHERE `key` = ?\", \"issue_id_mode\").Scan(&idMode)\n\tif err != nil && err != sql.ErrNoRows {","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/issues.go#L870-L906","documentation":"When seeding found no existing numeric issue IDs, the code inserts the very first counter row `INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)`. This error wraps failure of that INSERT: primary-key conflict (another writer inserted the row in the meantime), schema/table missing, constraint violation, or transaction abort.","triggerScenarios":"First-ever counter-mode ID creation for a prefix in an empty database, where the initial INSERT fails — most commonly because a concurrent creator already inserted the same prefix row (duplicate-key error), or the issue_counter table doesn't exist.","commonSituations":"Two bd processes initializing a fresh repository simultaneously; a repository created by a newer beads version against an older schema without issue_counter; read-only or full-disk storage backends rejecting the write.","solutions":["Check the wrapped cause: if it is a duplicate-key error, the row now exists — just retry the operation (the counter is already initialized).","Run the schema migration to ensure issue_counter exists, then retry.","Serialize repository initialization: initialize once with a single bd process before running parallel commands.","Check storage writability (disk space, read-only mount, server permissions) if the insert persists in failing."],"exampleFix":"// before\n_, err = tx.ExecContext(ctx, \"INSERT INTO issue_counter (prefix, last_id) VALUES (?, 1)\", prefix)\nif err != nil {\n    return \"\", fmt.Errorf(\"failed to insert initial issue counter for prefix %q: %w\", prefix, err)\n}\n// after (tolerate a concurrent initializer)\nif _, err = tx.ExecContext(ctx, \"INSERT IGNORE INTO issue_counter (prefix, last_id) VALUES (?, 1)\", prefix); err != nil {\n    return \"\", fmt.Errorf(\"failed to insert initial issue counter for prefix %q: %w\", prefix, err)\n}","handlingStrategy":"retry","validationCode":"var n int\nif err := db.QueryRow(\"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'issue_counter'\").Scan(&n); err != nil || n == 0 {\n    return fmt.Errorf(\"run schema migration before first counter-mode write\")\n}","typeGuard":"func isDuplicateKeyErr(err error) bool {\n    return err != nil && (strings.Contains(err.Error(), \"duplicate\") || strings.Contains(err.Error(), \"Duplicate entry\"))\n}","tryCatchPattern":"id, err := store.CreateIssue(ctx, issue)\nif err != nil && strings.Contains(err.Error(), \"insert initial issue counter\") {\n    if isDuplicateKeyErr(err) {\n        // another process initialized the counter; safe to retry\n        id, err = store.CreateIssue(ctx, issue)\n    }\n}","preventionTips":["Initialize new repositories with a single bd process before parallelizing","Ensure the issue_counter schema exists before enabling counter mode","Check storage writability (disk space, permissions) on first use of a repo","Use INSERT IGNORE/ON CONFLICT semantics when available in your beads version"],"tags":["database","counter","insert","race-condition"],"backgroundTag":"counter-insert-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}