Skip to content

fix: encode the Error PDU without spurious context tags#202

Merged
gralin merged 1 commit into
masterfrom
fix/error-pdu-plain-encoding
Jul 4, 2026
Merged

fix: encode the Error PDU without spurious context tags#202
gralin merged 1 commit into
masterfrom
fix/error-pdu-plain-encoding

Conversation

@gralin

@gralin gralin commented Jul 4, 2026

Copy link
Copy Markdown
Member

Fixes the malformed Error PDU described in #199: since 3.x, Services.EncodeError wrapped the error class/code pair in context tags by default, so every error sent by ErrorResponse violated ASHRAE 135 §20.1.7 (the BACnet-Error-PDU carries the plain Error production — two application-tagged enumerations, no enclosing context tags). Foreign stacks mis-decoded every error this library returned; the tolerant DecodeError hid it from C#↔C# round-trips. 2.0.4 was correct — the regression came in with 2f23aba, whose tag-wrapping is genuinely needed only for the trend-log failure log-datum.

Changes

  • Services.EncodeError: tagNumber is now nullable and opt-in — plain encoding by default.
  • The two call sites whose ASN.1 actually wraps the Error pass their tags explicitly: EncodePrivateTransferError (error-type [0]) and EncodeLogRecord (failure log-datum [8]). ErrorResponse gets the plain encoding without a call-site change.
  • DecodeError stays tolerant (accepts both plain and legacy-wrapped forms), so errors from peers running affected 3.x/4.0-beta versions still decode.
  • Regression tests assert raw wire bytes — deliberately not round-tripping through the tolerant decoder, which is exactly how this went unnoticed.

Verification

Test (stock bacrp via router) 4.0.0-beta.1 / master this branch
read analog-value 999 (unknown object) BACnet Error: services: success BACnet Error: object: unknown-object
read unknown property on device object BACnet Error: services: success BACnet Error: property: unknown-property
normal read sanity ✅ unchanged

Independent of #198 (no shared files apart from a trivial CHANGELOG adjacency).

Closes #199.

Since 3.x (2f23aba) Services.EncodeError wrapped the error-class and
error-code enumerations in context tags by default, so every Error PDU
sent by ErrorResponse was malformed: the BACnet-Error-PDU carries the
plain Error production - two application-tagged enumerations with no
enclosing context tags (ASHRAE 135 20.1.7). Foreign stacks mis-decoded
every error this library returned (bacnet-stack displayed
"services: success" instead of "object: unknown-object"); the tolerant
DecodeError hid the regression from same-stack round-trips.

The wrapping is now opt-in and explicit at the call sites whose ASN.1
actually requires it: ConfirmedPrivateTransfer-Error error-type [0] and
the trend-log failure log-datum [8]. DecodeError stays tolerant so
errors from peers running affected versions still decode.

The regression tests assert raw wire bytes on purpose - verifying the
encoder through the tolerant decoder is how this went unnoticed.
@gralin gralin merged commit 4c3d281 into master Jul 4, 2026
4 checks passed
@gralin gralin deleted the fix/error-pdu-plain-encoding branch July 4, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ErrorResponse encodes the Error PDU with spurious context tags (regression since 3.x, breaks interop with bacnet-stack)

1 participant