{"record":{"id":"e33f53551ba53cdc","repo":"tursodatabase/turso","slug":"missing-parameter-values-for-parameters","errorCode":null,"errorMessage":"Missing parameter values for {parameters}.","messagePattern":"Missing parameter values for (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs","lineNumber":671,"sourceCode":"            if (string.IsNullOrEmpty(parameter.ParameterName))\n                throw new InvalidOperationException(Properties.Resources.RequiresSet(nameof(parameter.ParameterName)));\n            if (!parameter.HasValue)\n                throw new InvalidOperationException(Properties.Resources.RequiresSet(nameof(parameter.Value)));\n\n            var parameterIndex = FindParameterIndex(statement, parameter.ParameterName, parameterCount);\n            if (parameterIndex == 0)\n                continue;\n\n            TursoBindings.BindParameter(statement, parameterIndex, parameter.ToTursoValue());\n            boundParameters[parameterIndex] = true;\n        }\n\n        for (var i = 1; i <= parameterCount; i++)\n        {\n            if (!boundParameters[i])\n            {\n                var parameterName = TursoBindings.GetParameterName(statement, i);\n                throw new InvalidOperationException(\n                    parameterName is null\n                        ? Properties.Resources.MissingParameters(i)\n                        : Properties.Resources.MissingParameters(parameterName));\n            }\n        }\n    }\n\n    private static bool IsEmptyCommand(string commandText)\n    {\n        foreach (var line in commandText.Split('\\n'))\n        {\n            var trimmedLine = line.Trim();\n            if (trimmedLine.Length != 0 && !trimmedLine.StartsWith(\"--\", StringComparison.Ordinal))\n                return false;\n        }\n\n        return true;\n    }","sourceCodeStart":653,"sourceCodeEnd":689,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs#L653-L689","documentation":"Thrown when a prepared statement still has unbound SQL parameters after SqliteCommand bound everything in its Parameters collection. SQLite requires every placeholder (@name, $name, :name, or positional ?) in a statement to receive a value before execution, and Turso.Data.Sqlite enforces this at bind time (SqliteCommand.BindParameters). Note that extra parameters in the collection that match no placeholder are silently skipped, so a typo'd parameter name surfaces here as a 'missing' parameter, not as 'parameter not found'. The message names the unbound parameter, or its 1-based ordinal when the placeholder is positional (unnamed).","triggerScenarios":"Executing 'SELECT * FROM t WHERE id = @id' without cmd.Parameters.Add(new SqliteParameter(\"id\", value)); misspelling the parameter name relative to the SQL text (e.g. SQL uses @userId but code adds \"uid\"); dynamically building a WHERE clause with new placeholders while reusing a stale Parameters collection; using positional '?' placeholders where the statement declares more placeholders than parameters supplied; forgetting that each statement in a multi-statement batch contributes its own placeholders.","commonSituations":"Dynamic query builders (filter appended but parameter forgotten), copy-paste SQL with renamed placeholders, migrating code from providers that tolerate missing parameters by binding NULL, and ORM/query-helper bugs that cache a command but reset only part of its Parameters.","solutions":["Add a SqliteParameter for every placeholder in the SQL text, with a name matching exactly (prefix characters @/:/$ on the SqliteParameter are tolerated, the base name must match).","If the SQL was built dynamically, log the final CommandText at the error site and diff its placeholders against cmd.Parameters to find the mismatch.","For positional '?' placeholders, ensure the number of parameters added is at least the number of placeholders in the statement.","Null out stale parameters (cmd.Parameters.Clear()) before reusing a cached SqliteCommand so leftovers do not mask the real set."],"exampleFix":"// before\ncmd.CommandText = \"SELECT * FROM users WHERE id = @id AND name = @name\";\ncmd.Parameters.AddWithValue(\"id\", 42);\ncmd.ExecuteReader(); // throws: Missing parameter values for @name\n\n// after\ncmd.CommandText = \"SELECT * FROM users WHERE id = @id AND name = @name\";\ncmd.Parameters.AddWithValue(\"id\", 42);\ncmd.Parameters.AddWithValue(\"name\", \"alice\");\ncmd.ExecuteReader();","handlingStrategy":"validation","validationCode":"static readonly Regex ParamRegex = new(@\"[@:$]\\w+\", RegexOptions.Compiled);\n\nstatic void EnsureParametersBound(SqliteCommand cmd)\n{\n    var declared = ParamRegex.Matches(cmd.CommandText)\n        .Select(m => m.Value.TrimStart('@', ':', '$'))\n        .Where(n => n.Length > 0)\n        .Distinct(StringComparer.Ordinal)\n        .ToHashSet(StringComparer.Ordinal);\n    var supplied = cmd.Parameters.Cast<SqliteParameter>()\n        .Select(p => p.ParameterName.TrimStart('@', ':', '$'))\n        .ToHashSet(StringComparer.Ordinal);\n    var missing = declared.Where(n => !supplied.Contains(n)).ToList();\n    if (declared.Count == 0 && cmd.Parameters.Count > 0)\n        return; // positional '?' SQL: ensure counts match instead\n    if (missing.Count > 0)\n        throw new InvalidOperationException($\"Unbound SQL parameters: {string.Join(\", \", missing)}\");\n}\n\n// usage: EnsureParametersBound(cmd) before cmd.ExecuteReader()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive parameter names from the same constant/format string used to build the SQL, never retype them.","Call cmd.Parameters.Clear() before populating a reused command so stale entries cannot hide gaps.","Run a debug assertion comparing CommandText placeholders against Parameters at dev time.","Prefer named parameters over positional '?' so mismatches produce a name in the error, not an ordinal."],"tags":["ado-net","sqlite","turso","parameters","sql"],"backgroundTag":"missing-sql-parameter","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}