tursodatabase/turso · error · InvalidOperationException
Column name {name} is ambiguous between {column1} and {colum
Error message
Column name {name} is ambiguous between {column1} and {column2}. What it means
InvalidOperationException thrown by GetOrdinal when the exact case-sensitive lookup finds nothing but the case-insensitive fallback matches two or more columns whose names differ only by casing (e.g. 'ID' and 'id' from a JOIN). The reader first tries an ordinal match, then collects case-insensitive matches; more than one is ambiguous because the caller's intent cannot be determined. The message names both conflicting columns.
Source
Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteDataReader.cs:380
public override int GetOrdinal(string name)
{
EnsureOpen();
ArgumentNullException.ThrowIfNull(name);
_ = GetStatement();
for (var i = 0; i < FieldCount; i++)
{
if (string.Equals(GetName(i), name, StringComparison.Ordinal))
return i;
}
string? match = null;
var matchOrdinal = -1;
for (var i = 0; i < FieldCount; i++)
{
if (string.Equals(GetName(i), name, StringComparison.OrdinalIgnoreCase))
{
if (match is not null)
throw new InvalidOperationException(Properties.Resources.AmbiguousColumnName(name, match, GetName(i)));
match = GetName(i);
matchOrdinal = i;
}
}
if (match is not null)
return matchOrdinal;
throw new ArgumentOutOfRangeException(nameof(name), name, $"Column {name} was not found.");
}
public override DataTable GetSchemaTable()
{
EnsureOpen();
var statement = GetStatement();
var schema = new DataTable("SchemaTable");
schema.Columns.Add(SchemaTableColumn.ColumnName, typeof(string));View on GitHub (pinned to 244cde92a7)
Solutions
- Use the exact casing of the intended column — the ordinal (case-sensitive) pass runs first and returns immediately.
- Alias the duplicated columns in the query: SELECT a.Id AS a_id, b.id AS b_id, then GetOrdinal("a_id").
- As a last resort, resolve by ordinal (SELECT column order) or via reader.GetName(i) scans.
Example fix
// before
SELECT a.Id, b.id FROM orders a JOIN users b ON ...;
var i = reader.GetOrdinal("id"); // ambiguous
// after
SELECT a.Id AS order_id, b.id AS user_id FROM orders a JOIN users b ON ...;
var i = reader.GetOrdinal("user_id"); Defensive patterns
Strategy: validation
Validate before calling
int ResolveExact(SqliteDataReader r, string name)
{
var hits = Enumerable.Range(0, r.FieldCount)
.Where(i => string.Equals(r.GetName(i), name, StringComparison.OrdinalIgnoreCase)).ToList();
if (hits.Count > 1) throw new InvalidOperationException($"'{name}' matches {hits.Count} columns; use exact case or alias.");
return hits.Count == 1 ? hits[0] : throw new ArgumentOutOfRangeException(nameof(name), name);
} Try / catch
try { var i = reader.GetOrdinal(name); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ambiguous"))
{ /* switch to exact-case name or a query alias */ } Prevention
- Alias every column in multi-table JOINs so names are unique.
- Standardize identifier casing in your schema (one convention everywhere).
When it happens
Trigger: SELECT a.Id, b.id FROM t a JOIN u b ... then GetOrdinal("id"); queries over quoted mixed-case identifiers created with "MyColumn" vs MYCOLUMN; SQLite's ASCII case-insensitive identifier rules letting two such columns coexist.
Common situations: Joining tables with inconsistent identifier casing conventions; databases originally created on case-sensitive filesystems or ported from other engines; ORM-generated joins exposing both spellings in one projection.
Related errors
- Column {name} was not found.
- The data is NULL at ordinal {ordinal}.
- No data exists for the row/column.
- {method} requires an open data reader.
- 1
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/6daa69400fc3d6c2.
Report an issue: GitHub.