Ora 01036 Illegal Variable Name Number

12 min read

What "ORA-01036: Illegal Variable Name/Number" Actually Means

You wrote a query. Here's the thing — it looked fine. Day to day, maybe even ran fine in another tool. Then Oracle throws ORA-01036 at you and the whole thing grinds to a halt. That said, annoying? Yes. But it's not a mystery error — it's Oracle telling you it doesn't like how you named (or numbered) something you passed in.

It sounds simple, but the gap is usually here Simple, but easy to overlook..

This error almost always shows up in one of two places: parameterized queries using OleDb, ODP.So naturally, nET, or Oracle. ManagedDataAccess, and dynamic SQL where bind variables are being constructed in code. The driver receives a parameter, looks at its name, and rejects it because the name violates Oracle's rules, or the value is being treated as a variable name when it should be a value, or vice versa.

Here's the thing most people miss on the first encounter: ORA-01036 is rarely about the SQL itself. Still, your SELECT, INSERT, UPDATE, or DELETE is usually perfectly valid Oracle syntax. The problem lives in the binding layer — the bit of code that hands parameters to the database. Once you understand that, the fix becomes much less random.

Why This Error Is So Confusing

The message "illegal variable name/number" sounds like Oracle is parsing your SQL. It's parsing the parameter collection you attached to the command. It's not. And the rules for parameter names in code are stricter, in a few annoying ways, than the rules for identifiers inside actual SQL.

It sounds simple, but the gap is usually here.

On top of that, the error often appears only at execution time, not at compile time. Your code builds happily, the IDE doesn't complain, and the crash happens on the first ExecuteNonQuery or ExecuteReader call. That delay makes it look like a data problem when it's really a binding problem The details matter here..

Real talk — this step gets skipped all the time.

The other reason this trips people up: the same root cause can produce different symptoms depending on the driver. With Oracle.With OLE DB, you might see ORA-01036. But with ODP. NET unmanaged, you might see ORA-01036 plus* an ORA-01008 or a "not all variables bound" message. ManagedDataAccess, the inner exception sometimes points to a parameter index out of range. Same bug, different masks.

How Parameter Binding Works (And Where It Goes Wrong)

Naming Rules Oracle Actually Enforces

Oracle bind variables must start with a letter, and after that they can contain letters, digits, dollar signs, underscores, and hash signs. They cannot be reserved words, and they cannot be longer than 30 characters in older versions (longer in newer ones, but don't push it) But it adds up..

The catch with the .If you do include it, some drivers strip it, and some don't. A leading colon is required in SQL (:customerId), but in your code's parameter object, you usually do not include the colon. NET and OLE DB drivers is that they also* enforce their own rules on top. The ones that don't will pass :customerId to Oracle, which then complains because colons aren't legal at the start of a variable name when it's not in SQL text Not complicated — just consistent. Took long enough..

So one of the most common triggers is just that: the parameter is named :foo in code instead of foo, and the driver forwards it raw And it works..

The "Number" Half of the Error

That second word in the error — "Number" — is doing real work. In real terms, oracle bind variables are positional and named, depending on context. If you reference :1 somewhere in your SQL but never define a parameter at index 1, you get this error. If you reference :7 and only pass five parameters, same thing.

This happens most often when SQL is built dynamically. Also, a loop adds conditions like AND status = :s1, AND region = :s2, AND created_date > :s3, but the parameter list in code only has two of them. Or the count is right but the indexes got renumbered after a refactor and nobody updated the binding code That's the part that actually makes a difference..

Reserved Words Sneaking In

Another quiet cause: someone names a parameter number, date, value, row, level, size, or user. These look like normal words in C# or VB. And they're reserved in Oracle. The driver passes them through, Oracle parses the SQL, and rejects the whole thing.

The fix is boring and effective: prefix your parameters. So a little. On top of that, ugly? :p_customer_id, :p_status, :p_created_date. But you'll never collide with a reserved word, and the SQL stays readable Small thing, real impact..

Common Mistakes People Make With This Error

Mistake 1: Adding the Colon to the Parameter Name

This is the single biggest source of ORA-01036 I've seen in code review. The SQL has :customerId, the developer writes cmd.This leads to parameters. Add(":customerId", value), and Oracle chokes. Strip the colon in the parameter object. Always But it adds up..

Mistake 2: Mismatched Indexes in Dynamic SQL

The SQL has seven bind variables, the code passes seven parameters, but they're at the wrong positions. The SQL says :3 and the parameter at index 3 is named for a different variable. This is a logic bug, not a syntax bug, and it won't show up until runtime.

A useful habit: when you generate bind variables in a loop, generate the parameter objects in the same loop, in the same order, with the same naming convention. If you can't, build a dictionary that maps variable names to values and validate it before execution Small thing, real impact. Still holds up..

Short version: it depends. Long version — keep reading.

Mistake 3: Reusing Command Objects

You build a command, run it, then change the SQL and run it again without clearing the parameters. The old parameters are still there, with their old names, attached to a new query that doesn't reference them. Oracle sees unbound variables in the new SQL (or extra parameters it wasn't expecting, depending on the driver) and throws ORA-01036 Easy to understand, harder to ignore..

cmd.Still, parameters. Clear() is your friend. Call it whenever you change the command text.

Mistake 4: Assuming the Driver Will Tolerate Quirky Names

Some drivers quietly accept parameter names with spaces, dots, or other odd characters. In practice, nET historically has been stricter. If your code is migrating from SQL Server to Oracle, the parameter naming conventions that "just worked" in System.ODP.Day to day, sqlClient can suddenly fail. Data.SQL Server's @ prefix and Oracle's : prefix look similar but behave differently when round-tripped through the driver Worth keeping that in mind..

Practical Tips That Actually Help

Name your bind variables the same as your parameters. If your SQL uses :p_customer_id, your parameter object should be named p_customer_id. Sounds obvious, but in larger codebases you often see :cust in the SQL and customerId in the binding. It works until someone tweaks one and not the other Most people skip this — try not to. Simple as that..

Validate parameter counts before execution. Especially with dynamic SQL. A quick check — does the number of : references in the SQL (excluding ::, which is an escaped literal colon) match the number of parameters? — catches most of these bugs before they reach the database Worth knowing..

Use bind-by-name consistently. Oracle supports both positional (:1, :2) and named (:customerId) binding. Don't mix them. If your SQL uses named, the driver configuration should reflect that. In ODP.NET, this often comes down to setting BindByName = true on the command. When that's left at its default, the driver binds by position, and your perfectly named parameters get silently reordered That's the whole idea..

Log the SQL and the parameter list at debug level. When ORA-01036 fires, you want to see exactly what was sent. The exception message is rarely enough. A simple Log.Debug(${content}quot;SQL: {cmd.CommandText}, Params: {string.Join(",", cmd.Parameters.Cast<...>().Select(p => p.ParameterName + "=" + p.Value))}") saves hours.

Test with a minimal repro. Strip the query down to one parameter, one column, one value. If ORA-01036 still fires, the problem is in the binding layer. If it goes away, you've got a mismatch between the SQL and the parameter collection, and you can add complexity back piece by piece until it breaks again.

FAQ

Is ORA-01036 always a parameter naming problem?

No. Even so, it can also fire when the parameter count is wrong, when bind-by-name is disabled and order doesn't match, or when reserved words are used as bind variable names. The error is really a catch-all for "something about the parameters you sent me doesn't add up That's the part that actually makes a difference..

Does

Does ORA‑01036 appear when using PL/SQL blocks with parameters?

Yes – ORA‑01036 can surface in PL/SQL when the bind variables declared in the block do not match the parameters you supply from the .NET side. Consider a simple procedure:

CREATE OR REPLACE PROCEDURE get_customer (
    p_cust_id   IN  NUMBER,
    p_name      OUT VARCHAR2,
    p_status    OUT VARCHAR2
) AS
BEGIN
    SELECT name, status
      INTO p_name, p_status
      FROM customers
      WHERE customer_id = p_cust_id;
END;

If you call this procedure with ODP.NET and accidentally map a parameter named :p_cust_id to the command but forget to register the two OUT parameters (:p_name and :p_status), Oracle will raise ORA‑01036 because it sees fewer bind variables than the procedure expects. The same rule that applies to plain SQL—every placeholder must have a matching parameter object—applies to PL/SQL as well.

When you need to retrieve OUT or IN OUT values, always add the appropriate OracleParameter with Direction = ParameterDirection.Still, output (or ParameterDirection. Because of that, inputOutput) and with the correct Oracle type (OracleDbType. Varchar2, OracleDbType.On top of that, int32, …). Mismatched types can also trigger ORA‑01036, especially when the driver tries to coerce a value that does not fit the declared bind variable.

Quick note before moving on Worth keeping that in mind..

Can duplicate parameter names cause ORA‑01036?

Absolutely. Although it sounds obvious, duplicate entries in the OracleParameterCollection are a surprisingly common source of ORA‑01036. The driver may treat the first occurrence as the canonical bind target, while subsequent duplicates are ignored or cause a conflict.

  • Dynamic SQL generation adds the same parameter twice (e.g., a missing WHERE clause condition

Duplicate parameter names – a silent trap

When dynamic SQL generation logic accidentally adds the same bind variable twice, Oracle sees more placeholders than distinct parameter objects, or it sees the same name multiple times with conflicting definitions. The driver may pick the first occurrence and ignore the rest, leaving the extra placeholder unbound and raising ORA‑01036 Less friction, more output..

// ❌ Bad – the same parameter is added twice
var cmd = new OracleCommand("SELECT * FROM orders WHERE customer_id = :id AND status = :id", conn);
cmd.Parameters.Add(new OracleParameter("id", OracleDbType.Int64, 101, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("id", OracleDbType.Varchar2, "Shipped", ParameterDirection.Input));

Oracle interprets the first :id as an Int64 and the second :id as a Varchar2. Since you supplied only one OracleParameter for each name (and they clash), the driver can’t resolve the second placeholder, resulting in the error No workaround needed..

// ✅ Good – each placeholder has its own distinct name
var cmd = new OracleCommand(
    "SELECT * FROM orders WHERE customer_id = :cust_id AND status = :order_status", conn);
cmd.Parameters.Add(new OracleParameter("cust_id", OracleDbType.Int64, 101, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("order_status", OracleDbType.Varchar2, "Shipped", ParameterDirection.Input));

If you truly need the same value in multiple places, create separate parameters with distinct names and set their values to the same source:

cmd.Parameters.Add(new OracleParameter("p1", OracleDbType.Int64, 101, ParameterDirection.Input));
cmd.Parameters.Add(new OracleParameter("p2", OracleDbType.Int64, 101, ParameterDirection.Input));

Ensuring consistent naming across the pipeline

  1. Adopt a naming convention – prefix every bind variable with p_ (e.g., p_cust_id). This makes it easy to spot duplicate names during code reviews.

  2. Centralise parameter creation – encapsulate SQL building in a helper that adds parameters from a dictionary or a strongly‑typed model. The helper can enforce uniqueness Simple as that..

  3. Validate before execution – a lightweight pre‑flight check can compare the number of placeholders (Regex.Matches(sql, @":\w+")) to the count of OracleParameter objects:

    int placeholders = Regex.Matches(sql, @":\w+").Count;
    if (placeholders != cmd.Parameters.In real terms, count)
        throw new InvalidOperationException(
            $"Placeholder count ({placeholders}) ! = parameter count ({cmd.Parameters.
    
    

Parameter direction mismatches

Even when names are unique, forgetting an OUT (or InputOutput) parameter for a PL/SQL block will cause ORA‑01036 because Oracle expects a bind variable that it can write to.

// Missing OUT parameter for p_name
cmd.Parameters.Add(new OracleParameter("p_cust_id", OracleDbType.Int64, custId, ParameterDirection.Input));
// cmd.Parameters.Add(new OracleParameter("p_name", OracleDbType.Varchar2, 50, Parameter

```csharp
// Missing OUT parameter for p_name
cmd.Parameters.Add(new OracleParameter("p_cust_id", OracleDbType.Int64, custId, ParameterDirection.Input));
// cmd.Parameters.Add(new OracleParameter("p_name", OracleDbType.Varchar2, 50, ParameterDirection.Output)); // ← Forgot this!
cmd.CommandText = "BEGIN get_customer_name(:p_cust_id, :p_name); END;";
cmd.ExecuteNonQuery(); // ORA-01036 – Oracle can't bind :p_name

The fix is straightforward: always declare output parameters with the correct ParameterDirection:

var pCustId = new OracleParameter("p_cust_id", OracleDbType.Int64, custId, ParameterDirection.Input));
var pName = new OracleParameter("p_name", OracleDbType.Varchar2, 50, ParameterDirection.Output));

cmd.Parameters.Add(pCustId);
cmd.Parameters.Add(pName);

cmd.CommandText = "BEGIN get_customer_name(:p_cust_id, :p_name); END;";
cmd.ExecuteNonQuery();

string customerName = pName.Value.ToString();

Debugging ORA-01036 with Oracle's tracing

When parameter issues persist despite following best practices, enable Oracle's client tracing. Add the following to your sqlnet.ora file (typically located in the Oracle client network admin directory):

trace_level_client = 16
trace_file_client = oracle_client.trc
trace_directory_client = C:\Oracle\Logs

A trace file will reveal exactly how Oracle is parsing bind variables and matching them to parameters, exposing subtle mismatches in type, size, or direction that might not surface in the higher-level exception message.

Summary of best practices

Issue Symptom Resolution
Duplicate parameter names ORA-01036 or incorrect binding Use unique names per placeholder
Missing OUT parameter ORA-01036 in PL/SQL calls Declare all bind variables, including outputs
Type mismatch ORA-01036 or data truncation Match OracleDbType to the actual column type
Size omission ORA-01036 or truncation Specify Size for variable-length types
Missing parameter entirely ORA-01036 Ensure 1:1 mapping of placeholders to parameters

Conclusion

ORA-01036 is fundamentally a binding problem—it signals that Oracle cannot reconcile the placeholders in your SQL with the parameters your application has prepared. By understanding how Oracle's ODP.NET driver matches bind variables by name, ensuring each placeholder has a distinct and correctly typed parameter, and validating parameter directions for stored procedures, you can eliminate this error from your codebase.

Adopt defensive coding practices: validate parameter counts before execution, enforce naming conventions across your team, and put to work Oracle's tracing utilities when the root cause remains elusive. With these strategies in place, your Oracle database interactions will be both strong and maintainable, freeing you to focus on delivering value rather than troubleshooting cryptic binding errors.

Fresh from the Desk

Just Finished

See Where It Goes

Topics That Connect

Thank you for reading about Ora 01036 Illegal Variable Name Number. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home