{"record":{"id":"62e9e604fa745499","repo":"gastownhall/beads","slug":"reading-dependencies-rows-for-migration-0053-id-ba","errorCode":null,"errorMessage":"reading dependencies rows for migration 0053 id backfill: %w","messagePattern":"reading dependencies rows for migration 0053 id backfill: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/migration_repairs.go","lineNumber":460,"sourceCode":"\treturn ensureDependenciesIDPrimaryKey(ctx, db)\n}\n\n// backfillDependenciesID fills in any dependencies.id still NULL with\n// depid.New(issue_id, target) -- the same deterministic id every insert path\n// and the post-migration rekeyDependencyIDs pass use (dep_id_backfill.go) --\n// so rows with real edges get a real, cross-clone-stable id rather than a\n// throwaway placeholder, and rekeyDependencyIDs finds nothing left to correct\n// afterwards. The `WHERE id IS NULL` scope (rather than every row) is what\n// makes re-entry after a partial prior run cheap and idempotent: a row this\n// function already backfilled, or one that already had an id, is untouched.\nfunc backfillDependenciesID(ctx context.Context, db DBConn) error {\n\trows, err := db.QueryContext(ctx, `\n\t\tSELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external\n\t\tFROM dependencies\n\t\tWHERE id IS NULL\n\t`)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"reading dependencies rows for migration 0053 id backfill: %w\", err)\n\t}\n\ttype edge struct {\n\t\tissueID                                              string\n\t\tdependsOnIssueID, dependsOnWispID, dependsOnExternal sql.NullString\n\t}\n\tvar edges []edge\n\tfor rows.Next() {\n\t\tvar e edge\n\t\tif err := rows.Scan(&e.issueID, &e.dependsOnIssueID, &e.dependsOnWispID, &e.dependsOnExternal); err != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn fmt.Errorf(\"scanning dependencies row for migration 0053 id backfill: %w\", err)\n\t\t}\n\t\tedges = append(edges, e)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn fmt.Errorf(\"iterating dependencies rows for migration 0053 id backfill: %w\", err)\n\t}\n\t_ = rows.Close()","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/migration_repairs.go#L442-L478","documentation":"This wraps failure of the QueryContext that selects dependencies rows with id IS NULL for the migration-0053 backfill. The query failed before any row was returned — connection, SQL, or privilege problem — so the repair cannot collect the edges needing IDs. It is distinct from per-row scan failures.","triggerScenarios":"ensureDependenciesIDColumn added dependencies.id (or found it present), then backfillDependenciesID's SELECT ... WHERE id IS NULL errors: connection dropped, dependencies table unreadable/missing, syntax-level failure from an incompatible server, or SELECT privilege denied.","commonSituations":"Database restart mid-repair; repair user lacking SELECT on dependencies; running against a server version that chokes on the query; table dropped/corrupted between migration steps.","solutions":["Check the wrapped driver error and restore connectivity/privileges","Verify the dependencies table exists and is queryable with the app credentials","Re-run the repair; the id check and backfill are idempotent","If the server rejects the SQL, confirm server version compatibility"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm dependencies is readable before the backfill\nvar n int\nif err := db.QueryRowContext(ctx, \"SELECT COUNT(*) FROM dependencies\").Scan(&n); err != nil {\n    return fmt.Errorf(\"dependencies unreadable, fix before repair: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := repairV53RigAndSplitTargets(ctx, db); err != nil {\n    if strings.Contains(err.Error(), \"reading dependencies rows for migration 0053\") {\n        var drv *mysql.MySQLError\n        if errors.As(err, &drv) && (drv.Number == 2006 || drv.Number == 2013) {\n            db = reconnect(db)\n            return repairV53RigAndSplitTargets(ctx, db)\n        }\n    }\n    return err\n}","preventionTips":["Ensure SELECT privilege on dependencies for the repair user","Ping/reconnect before long repair sequences","Confirm server version supports the repair's SQL","Keep the database online for the duration of the repair"],"tags":["database","backfill","mysql","query-failed","schema-migration"],"backgroundTag":"query-execution-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}