bytebase/bytebase · error
failed to resolve positional column reference $%s
Error message
failed to resolve positional column reference $%s
What it means
Before indexing into the positional columns, resolvePositionalRef must fetch the fields of the qualified (or unqualified) FROM table or outer CTE. If that lookup fails — e.g. no matching table for the qualifier — the underlying error is wrapped with this message.
Source
Thrown at backend/plugin/parser/snowflake/query_span_extractor.go:1403
// resolvePositionalRef resolves a positional column reference $N (optionally
// qualified, d.$1) to the N-th column of the FROM scope (or of the qualified
// relation), mirroring the legacy DOLLAR/Column_position branch: errors on a
// non-numeric, < 1, or out-of-range position.
func (q *querySpanExtractor) resolvePositionalRef(dollar *ast.DollarRef) (base.QuerySpanResult, error) {
position, err := strconv.Atoi(dollar.Name)
if err != nil {
return base.QuerySpanResult{}, errors.Wrapf(err, "failed to parse column position %q to integer", dollar.Name)
}
if position < 1 {
return base.QuerySpanResult{}, errors.Errorf("column position %d is invalid because it is less than 1", position)
}
var normalizedDatabaseName, normalizedSchemaName, normalizedTableName string
if dollar.Qualifier != nil {
normalizedDatabaseName, normalizedSchemaName, normalizedTableName = normalizeSnowflakeObjectName(dollar.Qualifier, "", "")
}
left, err := q.getAllFieldsOfTableInFromOrOuterCTE(normalizedDatabaseName, normalizedSchemaName, normalizedTableName)
if err != nil {
return base.QuerySpanResult{}, errors.Wrapf(err, "failed to resolve positional column reference $%s", dollar.Name)
}
if position > len(left) {
return base.QuerySpanResult{}, errors.Errorf("column position $%d is invalid: the FROM clause only returns %d columns", position, len(left))
}
return left[position-1], nil
}
// applyStarRenames applies the `SELECT * RENAME (col AS alias, ...)` transform
// to a star expansion: the matching result columns keep their lineage but take
// the alias as their output name (Snowflake returns the renamed column).
// Matching uses normalized identifiers, like the rest of the resolver.
func applyStarRenames(columns []base.QuerySpanResult, renames []ast.StarRename) []base.QuerySpanResult {
if len(renames) == 0 {
return columns
}
aliasByColumn := make(map[string]string, len(renames))
for _, r := range renames {
aliasByColumn[normalizeSnowflakeIdentifier(r.Col)] = normalizeSnowflakeIdentifier(r.Alias)View on GitHub (pinned to 1870550677)
Solutions
- Correct or drop the qualifier on the $n reference so it binds to an in-scope table
- Ensure the referenced table/CTE alias matches exactly (case after normalization)
- Inspect the wrapped cause ('no matching table ...') to identify which qualifier failed
Example fix
// before: SELECT o.$1 FROM orders -- alias 'o' undeclared SELECT orders.$1 FROM orders // after (or unqualified) SELECT $1 FROM orders
Defensive patterns
Strategy: try-catch
Validate before calling
if qualifier != nil && !scopeHasTable(qualifier) {
return errors.New("positional ref qualifier not in FROM scope")
} Try / catch
res, err := extractor.ExtractQuerySpan(stmt)
var serr *SpanError
if errors.As(err, &serr) && strings.Contains(err.Error(), "failed to resolve positional column reference") {
// fall back to unqualified $n or skip the column
} Prevention
- Only qualify $n refs with aliases present in FROM
- Prefer unqualified positional refs when a single table is in scope
- Inspect wrapped 'no matching table' causes for the offending qualifier
When it happens
Trigger: A qualified positional reference like t.$1 where table t is not in the FROM clause or outer CTE scope, producing the inner 'no matching table' error.
Common situations: Qualifying $1 with an alias that does not exist; referencing an outer CTE that is not visible at this nesting level.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- failed to parse column position %q to integer
- column position %d is invalid because it is less than 1
- column position $%d is invalid: the FROM clause only returns
- failed to extract sensitive fields of the recursive clause o
- the number of columns in the CTE %q returns %d fields, but t
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/902c48696e44f2b1.
Report an issue: GitHub.