{"record":{"id":"b7c626b1f0355abf","repo":"tursodatabase/turso","slug":"parameter-name-parametername-is-ambiguous","errorCode":null,"errorMessage":"Parameter name {parameterName} is ambiguous.","messagePattern":"Parameter name (.+?) is ambiguous\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs","lineNumber":1084,"sourceCode":"        }\n\n        return message;\n    }\n\n    private static int FindParameterIndex(TursoStatementHandle statement, string parameterName, int parameterCount)\n    {\n        var index = FindExactParameterIndex(statement, parameterName, parameterCount);\n        if (index != 0 || IsPrefixed(parameterName))\n            return index;\n\n        foreach (var prefix in new[] { '@', '$', ':' })\n        {\n            var prefixedIndex = FindExactParameterIndex(statement, prefix + parameterName, parameterCount);\n            if (prefixedIndex == 0)\n                continue;\n\n            if (index != 0)\n                throw new InvalidOperationException(Properties.Resources.AmbiguousParameterName(parameterName));\n\n            index = prefixedIndex;\n        }\n\n        return index;\n    }\n\n    private static int FindExactParameterIndex(TursoStatementHandle statement, string parameterName, int parameterCount)\n    {\n        for (var i = 1; i <= parameterCount; i++)\n        {\n            if (string.Equals(TursoBindings.GetParameterName(statement, i), parameterName, StringComparison.Ordinal))\n                return i;\n        }\n\n        return 0;\n    }\n","sourceCodeStart":1066,"sourceCodeEnd":1102,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs#L1066-L1102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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 $.","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."],"exampleFix":"// before\ncmd.CommandText = \"SELECT @foo + $foo\";\ncmd.Parameters.AddWithValue(\"foo\", 1); // throws: Parameter name foo is ambiguous\n\n// after\ncmd.CommandText = \"SELECT @foo + @foo2\";\ncmd.Parameters.AddWithValue(\"@foo\", 1);\ncmd.Parameters.AddWithValue(\"@foo2\", 2);","handlingStrategy":"validation","validationCode":"static readonly Regex PrefixParam = new(@\"[@:$](\\w+)\", RegexOptions.Compiled);\n\nstatic void EnsureNoAmbiguousParameters(string sql, string parameterName)\n{\n    if (parameterName.StartsWith('@') || parameterName.StartsWith('$') || parameterName.StartsWith(':'))\n        return; // prefixed names bind exactly and are never ambiguous\n    var base2 = parameterName.TrimStart('@', ':', '$');\n    var variants = PrefixParam.Matches(sql)\n        .Select(m => (prefix: m.Value[0], name: m.Groups[1].Value))\n        .Where(p => p.name == base2)\n        .Select(p => p.prefix)\n        .Distinct()\n        .ToList();\n    if (variants.Count > 1)\n        throw new InvalidOperationException($\"SQL declares {parameterName} with multiple prefixes: {string.Join(\", \", variants)}\");\n}","typeGuard":"static bool IsPrefixedParameterName(string name) => name.StartsWith('@') || name.StartsWith('$') || name.StartsWith(':');\n\n// Prefer always-supplied prefixed names so binding is exact-match and cannot be ambiguous.","tryCatchPattern":"null","preventionTips":["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."],"tags":["ado-net","sqlite","turso","parameters"],"backgroundTag":"ambiguous-parameter-binding","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}