{"record":{"id":"ba2d7ccd63480120","repo":"gastownhall/beads","slug":"schema-behind-drift-check-w","errorCode":null,"errorMessage":"schema behind-drift check: %w","messagePattern":"schema behind-drift check: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/schema.go","lineNumber":198,"sourceCode":"// *SchemaBehindError.\nfunc IsSchemaBehindError(err error) bool {\n\tvar e *SchemaBehindError\n\treturn errors.As(err, &e)\n}\n\n// CheckBehindDrift returns a *SchemaBehindError when the database's schema\n// version is behind the binary's. Used by read-only opens, which skip\n// MigrateUp by design (bd-6dnrw.32) — the paths that previously auto-migrated\n// foreign databases (GH#3231) now need a clear open-time failure instead of\n// unknown-column errors at query time. BD_IGNORE_SCHEMA_SKEW=1 downgrades it\n// to a warning, mirroring forward drift. A fresh DB (version 0) is reported\n// as behind too: it has no readable schema at all.\nfunc CheckBehindDrift(ctx context.Context, db *sql.DB) error {\n\tvar currentVersion int\n\tif err := db.QueryRowContext(ctx,\n\t\t\"SELECT COALESCE(MAX(version), 0) FROM schema_migrations\",\n\t).Scan(&currentVersion); err != nil {\n\t\treturn fmt.Errorf(\"schema behind-drift check: %w\", err)\n\t}\n\tif currentVersion >= LatestVersion() {\n\t\treturn nil\n\t}\n\tif os.Getenv(\"BD_IGNORE_SCHEMA_SKEW\") == \"1\" {\n\t\tfmt.Fprintf(os.Stderr,\n\t\t\t\"Warning: schema skew ignored — database (v%d) is behind binary (v%d) and was opened read-only; some queries may fail\\n\",\n\t\t\tcurrentVersion, LatestVersion())\n\t\treturn nil\n\t}\n\treturn &SchemaBehindError{DBVersion: currentVersion, BinaryVersion: LatestVersion()}\n}\n\ntype dirtyTableState struct {\n\tstaged bool\n}\n\nvar doltStatusTableNameRE = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/schema.go#L180-L216","documentation":"This error wraps a failure to read `MAX(version)` from `schema_migrations` during the behind-drift check. CheckBehindDrift runs on read-only open paths that cannot migrate; this wrapper means the drift check itself failed because the schema_migrations table could not be queried, not that the DB is behind.","triggerScenarios":"Calling CheckBehindDrift on a database where the `schema_migrations` SELECT fails: table missing/corrupt, connection error, context cancellation, or a Dolt working-set lock held by another process.","commonSituations":"Read-only open against a workspace mid-migration by another process; corrupted repo; Dolt server unavailable; networked Dolt with a dropped connection.","solutions":["Inspect the wrapped cause to identify the underlying query failure","Confirm the Dolt server is reachable and the repo's working set is clean (`dolt status`)","Wait for any concurrent migration process to finish, then retry","Restore from backup if schema_migrations is corrupt"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"var v int\nif err := db.QueryRowContext(ctx, \"SELECT COALESCE(MAX(version),0) FROM schema_migrations\").Scan(&v); err != nil {\n\treturn err // cannot even evaluate behind-drift\n}","typeGuard":"func isBehindDriftCheckFailure(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"behind-drift check:\")\n}","tryCatchPattern":"if err := CheckBehindDrift(ctx, db); err != nil {\n\tif schema.IsSchemaBehindError(err) {\n\t\t// run a write command once to migrate\n\t} else {\n\t\t// query failed: server/lock/corruption issue\n\t\treturn err\n\t}\n}","preventionTips":["Never open the same workspace read-only while another process migrates it","Run migrations before switching to read-only access paths","Verify `dolt status` is clean before read-only opens"],"tags":["schema","migration","read-only","version-check"],"backgroundTag":"schema-version-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}