{"record":{"id":"261eeee75919a43f","repo":"DapperLib/Dapper","slug":"when-passing-parameters-by-position-each-paramete","errorCode":null,"errorMessage":"When passing parameters by position, each parameter can only be referenced once","messagePattern":"When passing parameters by position, each parameter can only be referenced once","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Dapper/SqlMapper.cs","lineNumber":1923,"sourceCode":"        {\r\n            if (cmd.Parameters.Count == 0) return;\r\n\r\n            Dictionary<string, IDbDataParameter> parameters = new(StringComparer.Ordinal);\r\n\r\n            foreach (IDbDataParameter param in cmd.Parameters)\r\n            {\r\n                if (!string.IsNullOrEmpty(param.ParameterName)) parameters[param.ParameterName] = param;\r\n            }\r\n            var consumed = new HashSet<string>(StringComparer.Ordinal);\r\n            bool firstMatch = true;\r\n            int index = 0; // use this to spoof names; in most pseudo-positional cases, the name is ignored, however:\r\n                           // for \"snowflake\", the name needs to be incremental i.e. \"1\", \"2\", \"3\"\r\n            cmd.CommandText = CompiledRegex.PseudoPositional.Replace(cmd.CommandText, match =>\r\n            {\r\n                string key = match.Groups[1].Value;\r\n                if (!consumed.Add(key))\r\n                {\r\n                    throw new InvalidOperationException(\"When passing parameters by position, each parameter can only be referenced once\");\r\n                }\r\n                else if (parameters.TryGetValue(key, out IDbDataParameter? param))\r\n                {\r\n                    if (firstMatch)\r\n                    {\r\n                        firstMatch = false;\r\n                        cmd.Parameters.Clear(); // only clear if we are pretty positive that we've found this pattern successfully\r\n                    }\r\n                    // if found, return the anonymous token \"?\"\r\n                    if (Settings.UseIncrementalPseudoPositionalParameterNames)\r\n                    {\r\n                        param.ParameterName = (++index).ToString();\r\n                    }\r\n                    cmd.Parameters.Add(param);\r\n                    parameters.Remove(key);\r\n                    consumed.Add(key);\r\n                    return \"?\";\r\n                }\r","sourceCodeStart":1905,"sourceCodeEnd":1941,"githubUrl":"https://github.com/DapperLib/Dapper/blob/72a54c475f75e18cb93cba0809d00a5e6e49efd9/Dapper/SqlMapper.cs#L1905-L1941","documentation":"Thrown during pseudo-positional parameter rewriting when the same `?key?` token is consumed more than once in a single command. In pseudo-positional mode (used for providers like Snowflake, or when `Settings.PseudoPositional` is enabled) each positional placeholder maps to exactly one parameter and is consumed the first time it is matched; re-referencing it would produce an ambiguous/unsatisfiable binding. Dapper tracks consumed keys in a HashSet and rejects duplicates.","triggerScenarios":"Writing SQL like `where a = ?id? or b = ?id?` while the command is being processed by the pseudo-positional replacer (CompiledRegex.PseudoPositional). The second `?id?` cannot be re-consumed. Also triggered when a single named parameter is intentionally reused but the provider/dialect is configured for positional substitution.","commonSituations":"Migrating from a provider that allowed named-parameter reuse (e.g. SQL Server `@id ... @id`) to Snowflake or another positional dialect; enabling pseudo-positional mode globally; templating SQL that repeats a placeholder.","solutions":["Give each positional occurrence a unique key and supply a matching parameter for each, e.g. `where a = ?id1? or b = ?id2?`.","If you need the same value in two places, pass two parameters with the same value but different names.","Disable pseudo-positional mode if your provider supports true named parameters and reuse (`Settings.PseudoPositional`).","Rewrite the predicate to reference the parameter once, e.g. `where ?id? in (a, b)` or use OR on a single comparison."],"exampleFix":"// before (pseudo-positional, throws on second ?id?)\nvar sql = \"select * from t where a = ?id? or b = ?id?\";\n// after\nvar sql = \"select * from t where a = ?id1? or b = ?id2?\";\nvar p = new { id1 = id, id2 = id };","handlingStrategy":"validation","validationCode":"// Ensure each pseudo-positional token is unique in the SQL.\nstatic IEnumerable<string> PseudoPositionalKeys(string sql) =>\n    System.Text.RegularExpressions.Regex.Matches(sql, @\"\\?([^?]+)\\?\").Cast<Match>().Select(m => m.Groups[1].Value);\nvar dups = PseudoPositionalKeys(sql).GroupBy(k => k).Where(g => g.Count() > 1);\nif (dups.Any()) throw new InvalidOperationException(\"Duplicate pseudo-positional keys: \" + string.Join(\",\", dups.Select(g => g.Key)));","typeGuard":null,"tryCatchPattern":"try { var rows = cnn.Query<T>(sql, p); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"each parameter can only be referenced once\"))\n{ /* rewrite SQL so each ?key? is unique, supply one param per occurrence */ }","preventionTips":["Use unique placeholder names per occurrence in pseudo-positional SQL.","If you need the same value twice, pass it under multiple names.","Disable pseudo-positional mode when the provider supports named-parameter reuse."],"tags":["dapper","pseudo-positional","parameters","snowflake"],"backgroundTag":null,"analyzedSha":"72a54c475f75e18cb93cba0809d00a5e6e49efd9","analyzedAt":"2026-08-13T14:18:18.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}