tursodatabase/turso · error · InvalidOperationException
Parameter name {parameterName} is ambiguous.
Error message
Parameter name {parameterName} is ambiguous. What it means
SqliteCommand.FindParameterIndex resolves a parameter supplied without its prefix character by trying @name, $name, and :name variants against the statement's declared placeholders. If the statement declares more than one prefixed variant of the same base name (e.g. both @id and $id), resolution cannot tell which one you meant and throws InvalidOperationException. This only happens when the SqliteParameter name itself is unprefixed; a prefixed parameter name matches exactly one placeholder and never conflicts.
Source
Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs:1084
}
return message;
}
private static int FindParameterIndex(TursoStatementHandle statement, string parameterName, int parameterCount)
{
var index = FindExactParameterIndex(statement, parameterName, parameterCount);
if (index != 0 || IsPrefixed(parameterName))
return index;
foreach (var prefix in new[] { '@', '$', ':' })
{
var prefixedIndex = FindExactParameterIndex(statement, prefix + parameterName, parameterCount);
if (prefixedIndex == 0)
continue;
if (index != 0)
throw new InvalidOperationException(Properties.Resources.AmbiguousParameterName(parameterName));
index = prefixedIndex;
}
return index;
}
private static int FindExactParameterIndex(TursoStatementHandle statement, string parameterName, int parameterCount)
{
for (var i = 1; i <= parameterCount; i++)
{
if (string.Equals(TursoBindings.GetParameterName(statement, i), parameterName, StringComparison.Ordinal))
return i;
}
return 0;
}
View on GitHub (pinned to 6c72522679)
Solutions
- Rewrite the SQL to use one consistent prefix character for every placeholder of the same base name.
- Or supply the fully prefixed name in the SqliteParameter (e.g. "@foo" or "$foo") so it binds by exact match and cannot be ambiguous.
- If both variants are genuinely needed, add two SqliteParameter objects, each with its exact prefixed name.
Example fix
// before
cmd.CommandText = "SELECT @foo + $foo";
cmd.Parameters.AddWithValue("foo", 1); // throws: Parameter name foo is ambiguous
// after
cmd.CommandText = "SELECT @foo + @foo2";
cmd.Parameters.AddWithValue("@foo", 1);
cmd.Parameters.AddWithValue("@foo2", 2); Defensive patterns
Strategy: validation
Validate before calling
static readonly Regex PrefixParam = new(@"[@:$](\w+)", RegexOptions.Compiled);
static void EnsureNoAmbiguousParameters(string sql, string parameterName)
{
if (parameterName.StartsWith('@') || parameterName.StartsWith('$') || parameterName.StartsWith(':'))
return; // prefixed names bind exactly and are never ambiguous
var base2 = parameterName.TrimStart('@', ':', '$');
var variants = PrefixParam.Matches(sql)
.Select(m => (prefix: m.Value[0], name: m.Groups[1].Value))
.Where(p => p.name == base2)
.Select(p => p.prefix)
.Distinct()
.ToList();
if (variants.Count > 1)
throw new InvalidOperationException($"SQL declares {parameterName} with multiple prefixes: {string.Join(", ", variants)}");
} Type guard
static bool IsPrefixedParameterName(string name) => name.StartsWith('@') || name.StartsWith('$') || name.StartsWith(':');
// Prefer always-supplied prefixed names so binding is exact-match and cannot be ambiguous. Try / catch
null
Prevention
- Standardize the whole codebase on one prefix character (conventionally @).
- Always add SqliteParameter objects with the prefixed name exactly as written in the SQL.
- Lint generated SQL for mixed @/:/$ placeholders on the same base name.
When it happens
Trigger: SQL like 'SELECT @foo + $foo' while adding cmd.Parameters.AddWithValue("foo", 1); a statement mixing ':id' in one clause and '@id' in another (e.g. SQL assembled from fragments written by different authors) while binding "id"; test harnesses that concatenate prefix styles.
Common situations: Codebases that mix SQLite prefix conventions across files, generated SQL that injects parameters with a default prefix into hand-written SQL that uses a different one, and copy-paste between examples using @, :, and $.
Related errors
- Missing parameter values for {parameters}.
- Encryption is not supported by {libraryName}.
- Cannot access a disposed object. Object name: 'AggregateInv
- CollationRegistration
- Function {function} was called with a NULL value at ordinal
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20).
Data as JSON: /api/errors/b7c626b1f0355abf.
Report an issue: GitHub.