{"record":{"id":"6bb006a867a42fe4","repo":"gastownhall/beads","slug":"checking-wisp-dependencies-s-w","errorCode":null,"errorMessage":"checking wisp_dependencies.%s: %w","messagePattern":"checking wisp_dependencies\\.(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/migration_repairs.go","lineNumber":628,"sourceCode":"// depends_on_id (the legacy source the backfill reads from) is still around,\n// which is itself idempotent (each statement below is scoped to the rows it\n// hasn't yet filled in). Skipping this matters because a later ignored\n// migration (0005) drops depends_on_id once it assumes the split is done;\n// after that the source data needed to finish an interrupted backfill is\n// gone for good.\nfunc ensureWispDependenciesSplitTargets(ctx context.Context, db DBConn) error {\n\ttable, err := schemaTableExists(ctx, db, \"wisp_dependencies\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checking wisp_dependencies table: %w\", err)\n\t}\n\tif !table {\n\t\treturn nil\n\t}\n\n\tfor _, col := range wispDependenciesSplitTargetColumns() {\n\t\tpresent, err := schemaColumnExists(ctx, db, \"wisp_dependencies\", col.name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking wisp_dependencies.%s: %w\", col.name, err)\n\t\t}\n\t\tif !present {\n\t\t\tif _, err := db.ExecContext(ctx, \"ALTER TABLE wisp_dependencies ADD COLUMN \"+col.name+\" \"+col.definition); err != nil {\n\t\t\t\treturn fmt.Errorf(\"adding wisp_dependencies.%s for migration 0053: %w\", col.name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tlegacyTarget, err := schemaColumnExists(ctx, db, \"wisp_dependencies\", \"depends_on_id\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checking wisp_dependencies.depends_on_id: %w\", err)\n\t}\n\tif !legacyTarget {\n\t\t// Nothing left to backfill from: either a prior pass already\n\t\t// finished (depends_on_id has since been dropped) or this database\n\t\t// never had the legacy column to begin with.\n\t\treturn nil\n\t}","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/migration_repairs.go#L610-L646","documentation":"Wraps a failure from schemaColumnExists when checking whether one of the split-target columns (depends_on_issue_id, depends_on_wisp_id, depends_on_external) exists on wisp_dependencies via INFORMATION_SCHEMA.COLUMNS. Thrown by ensureWispDependenciesSplitTargets before it would ADD COLUMN anything, so the schema is left untouched. The underlying cause is a query failure, not a missing column (a missing column is handled by adding it).","triggerScenarios":"The repair loop iterates wispDependenciesSplitTargetColumns() and the INFORMATION_SCHEMA.COLUMNS count query errors — server unreachable, context cancelled mid-probe, or the database handle invalidated between statements.","commonSituations":"Long-running repair interrupted by context timeout; Dolt restart between the table check and the column check; read-only or restricted account that cannot query INFORMATION_SCHEMA.","solutions":["Check the wrapped driver error for the real cause (connection lost, timeout, permission).","Retry the migration repair — it is idempotent and re-probes all columns.","Ensure the process has network access to the Dolt server and the context deadline is generous enough for migration-time probes.","Verify the DB user can read INFORMATION_SCHEMA.COLUMNS."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check column presence outside the error path\nvar n int\ndb.QueryRowContext(ctx, `SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS\n  WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='wisp_dependencies'\n  AND COLUMN_NAME=?`, col).Scan(&n)","typeGuard":"func isSchemaProbeErr(err error) bool {\n    return strings.Contains(err.Error(), \"checking wisp_dependencies.\")\n}","tryCatchPattern":"if err := ensureWispDependenciesSplitTargets(ctx, db); err != nil {\n    if isRetryableDB(err) { time.Sleep(backoff); return ensureWispDependenciesSplitTargets(ctx, db) }\n    return err\n}","preventionTips":["Ensure the DB user can read INFORMATION_SCHEMA","Avoid cancelling the context mid-migration","Retry idempotent repairs on transient driver errors","Run repairs against a healthy, connected server"],"tags":["database","migration","schema-introspection"],"backgroundTag":"schema-introspection-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}