Skip to content

Error Reference

AmirHosseinMp02 edited this page Aug 1, 2026 · 1 revision

Error Reference

Every exception QueryForge raises, what causes it, and what to do. Grouped by type.


InvalidOperationException

"Query.Object must be specified before executing a query. Use ForObject(...) on the builder to set it."

Cause. A DapperQuery reached the provider with Object still null.

Fix. Set the target:

var dq = DapperQueryBuilder.FromBase(clientQuery)
    .ForObject("Users", "dbo", DapperObjectType.Table)   // ← this
    .Build();

Most often seen when a client body was bound directly as DapperQuery instead of Query. Bind Query and upgrade with FromBase — see Security.

"Query.Object.Name must be a non-empty object name."

Cause. Object exists but Name is empty or whitespace.

Fix. Supply a real name. If the name comes from configuration, check it is actually being read.

"A grouped query needs at least one GroupByColumn naming a real column of the target object."

Cause. A grouped compile method was called when no grouping column survives the whitelist.

Fix. In normal use you will not see this — the executor checks first and falls back to a flat result. It appears when calling SqlQueryCompiler directly. Check that your ColumnWhitelist contains the grouping column, and that its spelling matches the column the database reports.

"Stored procedures are executed directly rather than composed into a SELECT. This is handled by the execution path, not the compiler."

Cause. A compile method was called with DapperObjectType.SP.

Fix. Do not compile procedures. Execute them through IDapperQueryService or QueryForgeAsync, which route them down a separate path. See Dapper Provider.

"Cannot manage a connection because DapperQueryForgeOptions.ConnectionFactory was not configured in AddQueryForgeDapper(). Either configure it, or use the overload that takes an IDbConnection."

Cause. The connectionless QueryAsync(query) overload was called without a factory.

Fix. Either configure one:

builder.Services.AddQueryForgeDapper(options =>
{
    options.ConnectionFactory = sp =>
        new SqlConnection(sp.GetRequiredService<IConfiguration>().GetConnectionString("Default"));
});

or pass a connection:

await svc.QueryAsync<User>(connection, query);

"Call AddQueryForgeDapper() before AddQueryForgeDialect()."

Cause. AddQueryForgeDialect could not find a registry in the service collection.

Fix. Order the calls:

builder.Services.AddQueryForgeDapper();
builder.Services.AddQueryForgeDialect(new MyDialect());

NotSupportedException

"QueryForge cannot infer a database engine from connection type 'X'. Supported connections are SqlConnection, NpgsqlConnection, MySqlConnection, OracleConnection and SqliteConnection."

Cause. The connection's type name is not one QueryForge recognises. Common triggers: a profiling wrapper such as MiniProfiler's ProfiledDbConnection, a pooling decorator, a mock, or a driver whose connection class is named differently.

Fix. Unwrap the connection before handing it over, or register a dialect and give the type a recognised name. See Extending QueryForge.

Note the match is on the simple type name only — namespace and assembly are ignored — so a type you name SqlConnection resolves as SQL Server.

"No QueryForge dialect is registered for 'X'."

Cause. A connection resolved to a DapperDatabaseProvider with no dialect registered. Only possible if you built a registry yourself or replaced the default.

Fix. Call AddQueryForgeDapper(), which registers all five.

"MySQL has no table-valued functions." / "SQLite has no table-valued functions."

Cause. DapperObjectType.TVF against an engine without the concept.

Fix. Use a view, or a stored procedure on MySQL. On SQLite, a view.

"X has no table-valued functions, so DapperObjectType.TVF cannot be used with it."

The same thing, raised by the compiler rather than the dialect, when SupportsTableValuedFunctions is false.

"SQLite has no stored procedures."

Cause. DapperObjectType.SP against SQLite.

Fix. SQLite has no procedures at all. Use a view or query the table directly.

"X cannot return a result set from a stored procedure, so DapperObjectType.SP is not available for it."

Cause. DapperObjectType.SP against an engine whose SupportsStoredProcedures is false — Oracle or SQLite.

Fix. See the Oracle-specific message below.

"Oracle procedures return result sets through REF CURSOR output parameters. Wrap the logic in a pipelined function and query it with DapperObjectType.TVF instead."

Cause. DapperObjectType.SP against Oracle.

Fix. Exactly what the message says:

CREATE OR REPLACE FUNCTION get_user_report(p_include_deleted NUMBER)
RETURN user_report_tab PIPELINED AS ...
.ForObject("GET_USER_REPORT", "", DapperObjectType.TVF,
    new Dictionary<string, object?> { ["p_include_deleted"] = 0 })

QueryValidationException

"Query validation failed. See InvalidProperties for details."

Cause. Validate(..., QueryValidationMode.ThrowException) found at least one violation. This is working as designed, not a bug.

Handling. InvalidProperties lists every violation — all of them, not just the first:

catch (QueryValidationException ex)
{
    return Results.ValidationProblem(
        ex.InvalidProperties.Distinct().ToDictionary(p => p, p => new[] { "Denied by security policy" }));
}

Entry formats:

Violation Entry
select / sort / group-by / condition column the column name
page size too large "Paging.Size > 100"
page size too small "Paging.Size < 5"
missing object name "Object.Name is required."
disallowed schema "Object.Schema 'x' is not allowed."
disallowed object name "Object.Name 'x' is not allowed."

If the existence of a column is itself sensitive, do not echo InvalidProperties to the caller.


ArgumentException / ArgumentNullException

"The value cannot be an empty string or composed entirely of whitespace. (Parameter 'identifier')"

Cause. QuoteIdentifier received a blank name. The compiler never passes one, so this indicates either a custom dialect being called directly, or a ColumnWhitelist built with a blank entry.

Fix. Filter blanks out of any whitelist you construct yourself.

ArgumentNullException on source, query, rows, groups, dialect, services, connection

Standard null guards on public entry points. Pass the argument.


Errors from the database, surfaced through QueryForge

These are not QueryForge exceptions, but they show up in QueryForge call stacks.

ORA-00942: table or view does not exist

Oracle folds unquoted identifiers to upper case and QueryForge quotes what it is given. A table created as CREATE TABLE Users is stored as USERS.

.ForObject("USERS")     // ✅ matches what Oracle stored
.ForObject("Users")     // ❌ looks for "Users", which does not exist

ORA-01745: invalid host/bind variable name

An Oracle bind variable named after a reserved word. QueryForge's own parameters are p0, p1, … so this never comes from the library — check your own code for parameters named after columns such as Number, Level, Size or Date.

operator does not exist: integer > text (PostgreSQL)

A value was compared against a column of a different type without coercion. QueryForge coerces automatically using the type discovered from the result set, so this indicates the type was not discovered — for example a driver that could not describe the column, or a stale schema cache.

Fix. Restart the process to clear the cache if the table changed shape. If it persists, check whether the column is a computed or aliased expression whose type the driver cannot report.

Invalid object name 'dbo.X' (SQL Server)

The schema defaulted to dbo and the object lives elsewhere. Name the schema:

.ForObject("Users", "reporting")

Must declare the scalar variable "@p0"

Parameters were not bound. Almost always a custom execution path that bypassed SchemaCache.ToDapperParameters. Use IDapperQueryService or QueryForgeAsync.


Symptoms with no exception

The commonest support questions are not exceptions at all — QueryForge prefers dropping an unusable input to failing a request.

A filter is being ignored

Checklist, in order:

  1. Does the column exist on the target? Names are matched against the target's real columns and dropped if absent. Check spelling and — on Oracle — case of the object, though column names themselves are matched case-insensitively.
  2. Is the value missing? Every operator except Equals and NotEquals is dropped when Value is null. Between is also dropped without ValueTo.
  3. Is the operator a defined enum value? A JSON "operator": 47 is dropped.
  4. Did validation strip it? A denied column in SilentStrip mode disappears silently. Run the same query in ThrowException mode to find out.
  5. Is it a text operator on a non-string property? EF Core drops those.

A sort is being ignored

Same first and fourth checks. Also: a sort naming a column that is not in SelectColumns still works on Dapper and EF Core — the column is fetched for ordering even when not projected — so that is not the cause.

A grouped query came back flat

Every grouping column was dropped, so the provider fell back to flat. Check the column names against the target, and check rules.GroupBy(...) if you allow-list them.

A grouped query returns far more data than expected

Working as designed. Page size bounds the number of groups, and every row under those groups is returned so counts can be truthful. See Grouping and Hierarchies.

Newly added columns are not queryable

The schema cache is per-process and has no invalidation API. Restart the application.

Rows appear on two pages, or vanish between pages

The sort is not total, so the database is free to order ties differently between calls. Add a unique tie-breaker:

.Sort(new SortDescriptor("Score", SortOrder.Descending),
      new SortDescriptor("UserId"))

Results differ between the in-memory provider and the database

Almost always string case. In-memory text comparison is OrdinalIgnoreCase; the database follows the column's collation. See Cross-Provider Parity.

Every row comes back blank after adding SelectColumns

Should not happen — a selection naming nothing real is ignored, and a model that cannot be shaped is returned whole. If you see it, the model has a parameterless constructor and settable properties but the names do not match the properties. Check spelling against the property names, not the database column names.


Getting help

Include, at minimum:

  • the Query as JSON,
  • the provider and engine,
  • the SQL where relevant — ToQueryString() for EF Core, or SqlQueryCompiler directly for Dapper (see Dapper: Generated SQL),
  • and what you expected versus what you got.

Issues: https://github.com/PepperX-Dev/QueryForge/issues

Clone this wiki locally