dotnet/orleans · error · ArgumentException
The name must be a legal SQL table name
Error message
The name must be a legal SQL table name
What it means
Thrown by RelationalStorageExtensions.ExecuteMultipleInsertIntoAsync<T> when tableName is null, empty, or whitespace. The table name is interpolated directly into the 'INSERT INTO {0} (...)' template, so a blank value would produce invalid SQL; the extension rejects it early with ArgumentException(nameof(tableName)).
Source
Thrown at src/AdoNet/Shared/Storage/RelationalStorageExtensions.cs:57
private const string indexedParameterTemplate = "@p{0}";
/// <summary>
/// Executes a multi-record insert query clause with <em>SELECT UNION ALL</em>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="storage">The storage to use.</param>
/// <param name="tableName">The table name to against which to execute the query.</param>
/// <param name="parameters">The parameters to insert.</param>
/// <param name="nameMap">If provided, maps property names from <typeparamref name="T"/> to ones provided in the map.</param>
/// <param name="onlyOnceColumns">If given, SQL parameter values for the given <typeparamref name="T"/> property types are generated only once. Effective only when <paramref name="useSqlParams"/> is <em>TRUE</em>.</param>
/// <param name="useSqlParams"><em>TRUE</em> if the query should be in parameterized form. <em>FALSE</em> otherwise.</param>
/// <param name="cancellationToken">The cancellation token. Defaults to <see cref="CancellationToken.None"/>.</param>
/// <returns>The rows affected.</returns>
public static Task<int> ExecuteMultipleInsertIntoAsync<T>(this IRelationalStorage storage, string tableName, IEnumerable<T> parameters, IReadOnlyDictionary<string, string>? nameMap = null, IEnumerable<string>? onlyOnceColumns = null, bool useSqlParams = true, CancellationToken cancellationToken = default)
{
if(string.IsNullOrWhiteSpace(tableName))
{
throw new ArgumentException("The name must be a legal SQL table name", nameof(tableName));
}
if(parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}
var storageConsts = DbConstantsStore.GetDbConstants(storage.InvariantName);
var startEscapeIndicator = storageConsts.StartEscapeIndicator;
var endEscapeIndicator = storageConsts.EndEscapeIndicator;
//SqlParameters map is needed in case the query needs to be parameterized in order to avoid two
//reflection passes as first a query needs to be constructed and after that when a database
//command object has been created, parameters need to be provided to them.
var sqlParameters = new Dictionary<string, object?>();
const string insertIntoValuesTemplate = "INSERT INTO {0} ({1}) SELECT {2};";
var columns = string.Empty;View on GitHub (pinned to fca799fa70)
Solutions
- Pass a concrete, validated table name matching your schema (e.g. 'GrainState' or 'OrleansMembershipTable').
- Resolve table names from a mapping/registry and assert non-blank at startup.
- Use the storage-constant escape indicators correctly; the name is wrapped in start/end escape indicators by the extension.
- Add an integration test that inserts into each configured table to catch blank names early.
Example fix
// before
await storage.ExecuteMultipleInsertIntoAsync("", rows);
// after
const string table = "GrainState";
if (string.IsNullOrWhiteSpace(table)) throw new InvalidOperationException();
await storage.ExecuteMultipleInsertIntoAsync(table, rows); Defensive patterns
Strategy: validation
Validate before calling
if (string.IsNullOrWhiteSpace(tableName))
throw new InvalidOperationException("Table name is not configured."); Type guard
static bool IsValidTableName(string? s) => !string.IsNullOrWhiteSpace(s);
Try / catch
try { await storage.ExecuteMultipleInsertIntoAsync(table, rows); }
catch (ArgumentException ex) when (ex.ParamName == nameof(table)) { /* set table */ } Prevention
- Resolve table names from a registry and assert non-blank at startup.
- Cover each grain-state type with an integration test.
- Avoid computing table names from values that can be empty.
When it happens
Trigger: Calling storage.ExecuteMultipleInsertIntoAsync("", parameters) or with a null table name; typical when the table name is computed from a grain state type name or read from options that were not bound.
Common situations: Naming convention refactor that yields empty for some state types; options binding skipped so the table name option is null; per-grain storage where a mapping function returns empty for a new grain type.
Related errors
- Invalid offset length
- The name of invariant must contain characters
- Connection string must contain characters
- Configure exactly one of {nameof(connectionString)} or {name
- Value cannot be null. (Parameter 'query')
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/6b66bf10ab3341b5.
Report an issue: GitHub.