{"record":{"id":"f746e573bfca8635","repo":"gastownhall/beads","slug":"selecting-database-q-selector-returned-no-quoted","errorCode":null,"errorMessage":"selecting database %q: selector returned no quoted name","messagePattern":"selecting database %q: selector returned no quoted name","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/converged.go","lineNumber":154,"sourceCode":"\t}\n\n\tvar exists int\n\tif err := db.QueryRowContext(ctx,\n\t\t\"SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?\",\n\t\tdatabaseName,\n\t).Scan(&exists); err != nil {\n\t\treturn false, \"\", fmt.Errorf(\"probing database %q existence: %w\", databaseName, err)\n\t}\n\tif exists == 0 {\n\t\treturn false, \"\", nil\n\t}\n\n\tquoted, err := selector(ctx, db, databaseName)\n\tif err != nil {\n\t\treturn false, \"\", fmt.Errorf(\"selecting database %q: %w\", databaseName, err)\n\t}\n\tif quoted == \"\" {\n\t\treturn false, \"\", fmt.Errorf(\"selecting database %q: selector returned no quoted name\", databaseName)\n\t}\n\treturn true, quoted, nil\n}\n\n// migrationLockFree reports whether the database-scoped migration lock is\n// currently unheld. IS_FREE_LOCK is a read: it never queues, never acquires,\n// and costs one round trip, which is the entire point — the fast path exists\n// to stop paying GET_LOCK's queue.\n//\n// A NULL answer means the server would not tell us, which is not the same as\n// \"free\": fail closed.\nfunc migrationLockFree(ctx context.Context, db DBConn, lockName string) (bool, error) {\n\tvar free sql.NullInt64\n\tif err := db.QueryRowContext(ctx, \"SELECT IS_FREE_LOCK(?)\", lockName).Scan(&free); err != nil {\n\t\treturn false, fmt.Errorf(\"probing migration lock %q: %w\", lockName, err)\n\t}\n\tif !free.Valid {\n\t\treturn false, nil","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/converged.go#L136-L172","documentation":"A defensive error: after the DatabaseSelector reports success, it must return the identifier-quoted database name used to schema-qualify later reads (e.g. `mydb`.dolt_ignore). An empty string means the selector violated its contract, so selectTargetDatabase cannot safely qualify subsequent unqualified reads and fails loudly instead of silently misreading another database.","triggerScenarios":"A custom DatabaseSelector implementation returns (\"\", nil) — i.e. it claims success but does not return the quoted identifier. Only possible when the caller supplies a non-nil selector and the session was not already on the target database.","commonSituations":"A selector implementation that issues USE but forgets to build/return the quoted name; a selector returning the result of a helper that itself returns empty on some code path; writing a new selector for an embedded driver and mishandling the success branch.","solutions":["Fix the DatabaseSelector implementation to always return the backtick-quoted database name on success (e.g. fmt.Sprintf(\"`%s`\", name) after validating the name)","Check the selector's empty-return code path (early returns, helper functions returning \"\")","If using the library-provided selectors, report/upgrade — stock selectors always quote"],"exampleFix":"// before\nfunc mySelector(ctx context.Context, db DBConn, name string) (string, error) {\n    if _, err := db.ExecContext(ctx, \"USE \"+name); err != nil { return \"\", err }\n    return \"\", nil // BUG: forgot to return quoted name\n}\n// after\nfunc mySelector(ctx context.Context, db DBConn, name string) (string, error) {\n    quoted := \"`\" + name + \"`\"\n    if _, err := db.ExecContext(ctx, \"USE \"+quoted); err != nil { return \"\", err }\n    return quoted, nil\n}","handlingStrategy":"validation","validationCode":"// validate a custom selector's contract before installing it\nquoted, err := sel(ctx, db, \"testdb\")\nif err != nil || quoted == \"\" {\n    return fmt.Errorf(\"selector must return non-empty quoted name\")\n}","typeGuard":"func validSelector(sel DatabaseSelector) bool {\n    return sel != nil\n}\n// and assert its output: quoted != \"\" before proceeding","tryCatchPattern":null,"preventionTips":["Custom DatabaseSelector implementations must always return the backtick-quoted name on success","Unit-test any custom selector against the empty-string case","Prefer library-provided selectors"],"tags":["go","dolt","contract-violation","selector"],"backgroundTag":"selector-returned-empty-quoted-name","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}