{"record":{"id":"f6f1ac517bdecbe1","repo":"gastownhall/beads","slug":"probing-s-existence-w","errorCode":null,"errorMessage":"probing %s existence: %w","messagePattern":"probing (.+?) existence: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/schema.go","lineNumber":1220,"sourceCode":"\tif err != nil {\n\t\treturn false\n\t}\n\treturn current >= m.latest()\n}\n\nfunc (m migrationSource) currentVersion(ctx context.Context, db DBConn) (int, error) {\n\t// Probe existence with a query that always SUCCEEDS before ever issuing one\n\t// that can fail. A Dolt session that issues a failing statement stays\n\t// pinned to its pre-statement catalog snapshot, so a bare SELECT against a\n\t// not-yet-created cursor table poisons the pooled connection: tables\n\t// created afterwards on other connections stay invisible to this one for\n\t// the rest of its life in the pool (be-bv7x).\n\tvar cursorExists int\n\tif err := db.QueryRowContext(ctx,\n\t\t\"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?\",\n\t\tm.cursorTable,\n\t).Scan(&cursorExists); err != nil {\n\t\treturn 0, fmt.Errorf(\"probing %s existence: %w\", m.cursorTable, err)\n\t}\n\tif cursorExists == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar current int\n\terr := db.QueryRowContext(ctx, \"SELECT COALESCE(MAX(version), 0) FROM \"+m.cursorTable).Scan(&current)\n\tif err != nil && err != sql.ErrNoRows {\n\t\tif dberrors.IsTableNotExist(err) {\n\t\t\treturn 0, nil\n\t\t}\n\t\treturn 0, fmt.Errorf(\"reading %s version: %w\", m.cursorTable, err)\n\t}\n\tif current == 0 {\n\t\treturn 0, nil\n\t}\n\t// A missing cursor TABLE already meant \"nothing applied\". A cursor whose\n\t// tables are absent means the same thing and was previously believed","sourceCodeStart":1202,"sourceCodeEnd":1238,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/schema.go#L1202-L1238","documentation":"currentVersion first probes cursor-table existence with a guaranteed-to-succeed INFORMATION_SCHEMA COUNT(*) query — deliberately, because a failing statement poisons a pooled Dolt connection's catalog snapshot for the life of the session (be-bv7x). If even this safe probe fails, the error is wrapped as `probing <cursor> existence: <cause>`, indicating a connection/server-level fault rather than schema state.","triggerScenarios":"The `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?` scan fails: dead pooled connection, context timeout, permission denial on INFORMATION_SCHEMA, or server unavailability.","commonSituations":"App boots while the database is still starting (container orchestration race); stale pool connections after a server restart; wait_timeout reaping idle connections; wrong DSN database so DATABASE() is null/unexpected.","solutions":["Retry with fresh connections; if errors mention 'bad connection' or 'invalid connection', recycle the pool.","Verify the DSN names an existing schema so DATABASE() resolves correctly.","Add startup retry/backoff so MigrateUp waits for the DB to become reachable.","Confirm the DB user can read INFORMATION_SCHEMA (usually implicit, but restricted setups can deny it)."],"exampleFix":"// before\nif err := db.QueryRowContext(ctx, \"SELECT COUNT(*) FROM information_schema.tables WHERE ...\", m.cursorTable).Scan(&cursorExists); err != nil {\n    return 0, fmt.Errorf(\"probing %s existence: %w\", m.cursorTable, err)\n}\n// after\nif err := db.PingContext(ctx); err != nil {\n    return 0, fmt.Errorf(\"database unreachable before probing %s: %w\", m.cursorTable, err)\n}\nif err := db.QueryRowContext(ctx, \"SELECT COUNT(*) FROM information_schema.tables WHERE ...\", m.cursorTable).Scan(&cursorExists); err != nil {\n    return 0, fmt.Errorf(\"probing %s existence: %w\", m.cursorTable, err)\n}","handlingStrategy":"retry","validationCode":"// wait for the DB before starting migrations\nfor i := 0; i < 30; i++ {\n    if err := db.PingContext(ctx); err == nil { break }\n    time.Sleep(2 * time.Second)\n}","typeGuard":"func isConnErr(err error) bool {\n    var netErr net.Error\n    return errors.As(err, &netErr) || strings.Contains(err.Error(), \"connection refused\")\n}","tryCatchPattern":"err := MigrateUp(ctx, db)\nif err != nil && strings.Contains(err.Error(), \"probing \") {\n    // existence-probe failure is connectivity-level; wait and retry\n    time.Sleep(5 * time.Second)\n    err = MigrateUp(ctx, db)\n}","preventionTips":["Add startup readiness checks so migrations run only after the DB is reachable.","Verify the DSN includes a valid database name (DATABASE() must resolve).","Keep pool connection lifetimes below server wait_timeout.","Confirm the DB user can query information_schema."],"tags":["database","connection","migration"],"backgroundTag":"information-schema-probe-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}