{"record":{"id":"f6e026930f09c5b6","repo":"gastownhall/beads","slug":"backfill-custom-statuses-w","errorCode":null,"errorMessage":"backfill custom_statuses: %w","messagePattern":"backfill custom_statuses: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/backfill.go","lineNumber":21,"sourceCode":"import (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/steveyegge/beads/internal/storage/issueops\"\n\t\"github.com/steveyegge/beads/internal/types\"\n)\n\nfunc ensureBackfilledCustomStatusesCustomTypes(ctx context.Context, db DBConn) (bool, error) {\n\ttypesWrote, err := backfillCustomTypes(ctx, db)\n\tif err != nil {\n\t\treturn typesWrote, fmt.Errorf(\"backfill custom_types: %w\", err)\n\t}\n\tstatusesWrote, err := backfillCustomStatuses(ctx, db)\n\tif err != nil {\n\t\treturn typesWrote || statusesWrote, fmt.Errorf(\"backfill custom_statuses: %w\", err)\n\t}\n\treturn typesWrote || statusesWrote, nil\n}\n\nfunc needsBackfilledCustomStatusesCustomTypes(ctx context.Context, db DBConn) (bool, error) {\n\ttypesNeed, err := needsCustomTypesBackfill(ctx, db)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"custom_types: %w\", err)\n\t}\n\tstatusesNeed, err := needsCustomStatusesBackfill(ctx, db)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"custom_statuses: %w\", err)\n\t}\n\treturn typesNeed || statusesNeed, nil\n}\n\nfunc needsCustomTypesBackfill(ctx context.Context, db DBConn) (bool, error) {\n\tvar count int","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/backfill.go#L3-L39","documentation":"Wrapper from ensureBackfilledCustomStatusesCustomTypes when backfillCustomStatuses — seeding the custom_statuses table from configured custom statuses — fails. The raw driver error is wrapped with %w so the root cause (missing table, bad connection, permissions) stays intact. Raised during MigrateUp after the custom_types backfill step.","triggerScenarios":"backfillCustomStatuses errors on SELECT COUNT(*) FROM custom_statuses, reading the statuses config key, or INSERT IGNORE INTO custom_statuses (name, category) — typically because the table is missing, the connection dropped, or privileges are insufficient.","commonSituations":"Partially migrated database (custom_statuses not created yet); read-only or restricted DB user; concurrent lock on custom_statuses; server restart during upgrade.","solutions":["Re-run the migration; ensure all numbered migrations execute before the backfill so custom_statuses exists.","Restore connectivity or unlock the database and retry MigrateUp.","Grant the DB user SELECT/INSERT on custom_statuses and the config table.","Read the wrapped cause to target the exact statement that failed."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm custom_statuses exists before the migration backfill stage\nvar n int\nerr := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'custom_statuses'`).Scan(&n)\nif err == nil && n == 0 {\n\treturn fmt.Errorf(\"custom_statuses missing; run full migration chain first\")\n}","typeGuard":null,"tryCatchPattern":"err := migrateUp(ctx)\nif err != nil && strings.Contains(err.Error(), \"backfill custom_statuses:\") {\n\tif isTransient(err) {\n\t\terr = migrateUp(ctx) // seeding is idempotent (INSERT IGNORE, count-gated)\n\t}\n}","preventionTips":["Run migrations to completion before other tooling touches the DB","Grant the migration user INSERT on custom_statuses","Avoid killing `bd` mid-upgrade; let MigrateUp finish","Back up before schema upgrades"],"tags":["database","migration","backfill"],"backgroundTag":"schema-backfill-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}