BadHistoryOperationInvalid
BadPart 11 arguments
BadHistoryOperationInvalid means the server supports historical access but rejected the details structure you sent: an inconsistent time range, a mutually exclusive combination of start time, end time and numValuesPerNode, or a details type that does not match the operation. Fix the request parameters, not the server configuration.
What you actually see
The history service call itself completes but every node comes back with this code, and changing the time window by a few seconds sometimes makes it work.
Why it happens
- Illegal combination of startTime, endTime and numValuesPerNodeReadRawModifiedDetails allows you to omit one of startTime or endTime provided numValuesPerNode is set, which makes the read directional. Supplying neither bound, or omitting a bound with numValuesPerNode left at zero, leaves the request unbounded and the server rejects it outright.
- Inverted or zero-width time rangeAn endTime earlier than startTime is invalid rather than simply empty. This happens routinely when a client computes the window in local time and the server expects UTC, or when a shift boundary calculation rolls over midnight incorrectly.
- Wrong details type for the operationHistoryRead expects one of the read details structures and HistoryUpdate expects an update details structure. Passing an update details ExtensionObject to a read, or a raw details structure where processed details were intended, is a structural mismatch the server reports here.
- Processed read missing its mandatory fieldsReadProcessedDetails requires a processingInterval and an aggregateType per node in the request. A zero processing interval, or an aggregateType array whose length does not match nodesToRead, is invalid before any aggregate is even evaluated.
- Continuation point mixed with changed parametersWhen you continue a paged history read you must resend the identical details structure alongside the continuation point. Recomputing the time window between pages, or switching numValuesPerNode mid-sequence, invalidates the operation.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Log the exact details structure you sentPrint startTime, endTime, numValuesPerNode, returnBounds and the details type name before the call. Most occurrences of this code are visible immediately in that one line and need no server-side investigation at all.
- Send both bounds explicitly in UTCSet startTime and endTime as explicit UTC DateTimes, endTime strictly greater than startTime, and set numValuesPerNode to zero to mean unlimited within that window. Remove any reliance on directional reads while you are diagnosing.
- Respect MaxReturnDataValues instead of open windowsRead MaxReturnDataValues from HistoryServerCapabilities and page with continuation points rather than asking for an unbounded range. Keep the details structure byte-identical across every continuation call.
- Match the details type to the serviceFor a raw read use ReadRawModifiedDetails with isReadModified false; for values at fixed instants use ReadAtTimeDetails with a reqTimes array; for aggregates use ReadProcessedDetails with a non-zero processingInterval and one aggregateType per node.
- Validate array lengths against nodesToReadFor processed reads the aggregateType array must have exactly one entry per node in nodesToRead. Assert that in your client before the call; a length mismatch is a silent source of this code in loops that build requests dynamically.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| asyncua (Python) | Prefer node.read_raw_history(starttime, endtime) over hand-building HistoryReadDetails. When you do build details yourself, set ua.ReadRawModifiedDetails fields explicitly rather than relying on defaults, which leave both bounds unset. |
| node-opcua | session.readHistoryValue(nodes, start, end) constructs valid details for you. Hand-built HistoryReadRequest objects are where this code appears, usually with numValuesPerNode left at 0 and only one bound supplied. |
| UA-.NETStandard (C#) | HistoryRead takes an ExtensionObject wrapping the details; a type mismatch there compiles cleanly and fails at the server. The HistoryClient helper on the session validates far more of this before the wire. |
| open62541 (C) | UA_Client_HistoryRead_raw takes start and end as UA_DateTime; passing a zero value silently means 1601 and produces an enormous range that some servers reject as invalid rather than as too large. |
| UaExpert | The History Trend View builds valid details itself, so if UaExpert can read a range that your client cannot, the fault is in your request structure. Use it as the control case. |
What it looks like in the log
HistoryRead: BadHistoryOperationInvalid (startTime=2026-09-07T14:00:00Z endTime=2026-09-07T06:00:00Z numValuesPerNode=0)
ReadProcessedDetails rejected: processingInterval=0, aggregateType[] length 1 for 12 nodesToRead
Confirm the fix
The same nodes and the same time window return DataValues with SourceTimestamps inside the range, and a paged read completes through its continuation points without the code reappearing on page two.
Stop it happening again
Build history requests through one helper that asserts UTC, non-inverted bounds and matching array lengths, and that reuses the identical details structure across continuation calls. Never let a shift-window calculation reach the wire unvalidated.
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.