BadInvalidArgument
BadMethod calls
BadInvalidArgument means the argument count was right but at least one value was rejected: wrong data type, wrong array rank, out-of-range, or semantically invalid for the machine's current state. Read the inputArgumentResults array in CallMethodResult; it tells you which argument, position by position.
What you actually see
The call reaches the method and fails immediately, with the same values working from a vendor tool but not from your client.
Why it happens
- Data type does not match the declared ArgumentAn Int32 sent where UInt16 is declared, a Double where Float is declared, or a String where an Enumeration value is expected. The Variant type on the wire must match the Argument's DataType. Loosely typed clients infer a type from the language value and get this wrong routinely.
- Scalar sent where an array is declared, or vice versaThe Argument's ValueRank distinguishes a scalar from a one-dimensional array. A single-element array is not interchangeable with a scalar. This is the second most common cause and is invisible unless you log the Variant shape.
- Value outside the range the method acceptsA method can validate its inputs against engineering limits, an enumeration's legal values, a job identifier that must exist, or a recipe number within a configured span. A structurally perfect argument can still be semantically wrong.
- Structure argument encoded against the wrong definitionA Structure input is an ExtensionObject whose encoding must match the server's DataTypeDefinition. If your client built it from a stale definition, or from a legacy type dictionary while the server publishes DataTypeDefinition, the decode fails and the argument is invalid.
- Argument valid in itself but invalid for the current stateMethods on state machines often accept a value only in certain states: a start command with a job id while a job is already running, or a reset argument outside a fault state. Some servers report this as an invalid argument rather than as BadInvalidState.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Read inputArgumentResults, not just the statusCodeCallMethodResult carries a StatusCode per input argument plus diagnostic infos. That array pinpoints the offending position immediately. Clients that only surface the overall status throw away the most useful part of the response.
- Compare each Variant against the declared ArgumentRead the method's InputArguments property and, for each entry, log the declared DataType and ValueRank next to the Variant type and shape you are sending. Mismatches are obvious in that table and account for most cases.
- Wrap values in explicit VariantsStop relying on type inference. Construct each argument with the exact VariantType the Argument declares, including unsigned widths and Float versus Double, and build arrays explicitly with the declared rank.
- Re-read structure definitions at connectFor Structure arguments, load the server's DataTypeDefinition at session start and encode against that, rather than a definition captured at build time. Namespace and field order changes after a model update otherwise produce invalid arguments.
- Check the state preconditionRead the target object's CurrentState and any Executable or mode variables before calling. If the method is only legal in a particular state, make your client assert that first so the failure is reported as a state problem rather than an argument problem.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| node-opcua | Every input argument needs an explicit dataType and value. Build them from getArgumentDefinition and read inputArgumentResults on the response; node-opcua exposes it directly on the CallMethodResult. |
| asyncua (Python) | Python ints become Int64 by default. Wrap them as ua.Variant(value, ua.VariantType.UInt16) or similar to match the declaration; unsigned and narrow integer arguments are the usual failures. |
| UA-.NETStandard (C#) | Pass values with the exact CLR type matching the declared DataType, and inspect the returned inputArgumentResults collection. A ServiceResultException hides the per-argument detail unless you unwrap it. |
| open62541 (C) | Set the UA_Variant type explicitly with UA_Variant_setScalarCopy or UA_Variant_setArrayCopy and the right UA_TYPES index. A scalar set where an array is declared is the classic C-side cause. |
| UaExpert | The Call dialog types every field from the declaration, so a call that succeeds there and fails from code confirms a client-side typing problem. Note the exact values it used and reproduce them. |
What it looks like in the log
Call ns=4;i=7112 -> BadInvalidArgument; inputArgumentResults=[Good, BadTypeMismatch, Good]
argument 1 'Quantity' declared UInt32 ValueRank=-1, received Int64 array[1]
Confirm the fix
The call returns Good with every inputArgumentResults entry Good, and the method's observable effect happens on the machine. Repeat once from a cold state to prove the argument is valid independent of the state you tested in.
Stop it happening again
Type every argument explicitly from the server's declared Argument array, load structure definitions at connect rather than at build time, and always surface inputArgumentResults in your error messages so the next occurrence is diagnosed in one step.
Stuck on more than one of these? The troubleshooting clinic works through broken configurations end to end, on camera.
Troubleshooting clinic →Last verified 7 September 2026 against OPC UA 1.05.07. OPC UA Connect is an educational resource and is not affiliated with the OPC Foundation.