tursodatabase/turso · error · InvalidOperationException

Missing value for parameter {parameterName}.

Error message

Missing value for parameter {parameterName}.

What it means

The final Prepare() check reports a named placeholder (@name) in the statement that received no value from the collection: GetParameterName returns its name, so the error reads 'Missing value for parameter {name}.' (TursoCommand.cs:199-204).

Source

Thrown at bindings/dotnet/src/Turso.Data/TursoCommand.cs:209

                {
                    var parameterIndex = i + 1;
                    if (parameterIndex > parameterCount)
                        throw new InvalidOperationException($"Parameter at position {parameterIndex} was not found in the SQL statement.");

                    TursoBindings.BindParameter(preparedStatement, parameterIndex, parameter.ToValue());
                    boundParameters[parameterIndex] = true;
                }
            }

            for (var i = 1; i <= parameterCount; i++)
            {
                if (!boundParameters[i])
                {
                    var parameterName = TursoBindings.GetParameterName(preparedStatement, i);
                    throw new InvalidOperationException(
                        parameterName is null
                            ? $"Missing value for parameter at position {i}."
                            : $"Missing value for parameter {parameterName}.");
                }
            }

            _statement?.Dispose();
            _statement = preparedStatement;
            preparedStatement = null;
        }
        finally
        {
            preparedStatement?.Dispose();
        }
    }

    protected override DbParameter CreateDbParameter()
    {
        return new TursoParameter();
    }

View on GitHub (pinned to c1e5928725)

Solutions

  1. Add a parameter for every @name that appears in the SQL text.
  2. In dynamic SQL builders, pair each fragment with its value in one helper so they cannot diverge.
  3. Parse the @-tokens out of the final SQL and assert each has a matching collection entry (see validation snippet).

Example fix

// before
cmd.CommandText = "SELECT * FROM users WHERE id = @id AND tenant = @tenant";
cmd.Parameters.AddWithValue("@id", 42); // @tenant missing -> throws

// after
cmd.CommandText = "SELECT * FROM users WHERE id = @id AND tenant = @tenant";
cmd.Parameters.AddWithValue("@id", 42);
cmd.Parameters.AddWithValue("@tenant", tenantId);
Defensive patterns

Strategy: validation

Validate before calling

var required = Regex.Matches(cmd.CommandText, @"(?<![@\w])@(\w+)").Select(m => m.Groups[1].Value).ToHashSet(StringComparer.OrdinalIgnoreCase);
var provided = cmd.Parameters.Cast<TursoParameter>()
    .Where(p => !string.IsNullOrEmpty(p.ParameterName))
    .Select(p => p.ParameterName.TrimStart('@'));
var missing = required.Except(provided, StringComparer.OrdinalIgnoreCase).ToList();
if (missing.Count > 0)
    throw new InvalidOperationException($"Missing values for: {string.Join(", ", missing)}");

Try / catch

try
{
    cmd.Prepare();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Missing value for parameter"))
{
    // The message names the placeholder; add the matching AddWithValue call and retry.
}

Prevention

When it happens

Trigger: SQL "SELECT * FROM users WHERE id = @id AND tenant = @tenant" while only @id was added to cmd.Parameters; adding a parameter with an empty ParameterName so it was treated as positional.

Common situations: A new WHERE clause added to SQL without a matching AddWithValue; optional-filter builder code where the SQL fragment and the parameter append are out of sync; prefix or casing mismatch between the SQL token and the collection name.

Related errors


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20). Data as JSON: /api/errors/3dddb13d87c61d5b. Report an issue: GitHub.