diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs
index 376bbe4b..9ffd8b19 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs
@@ -191,8 +191,10 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
///
/// The device the struct lives on.
/// The discovered struct type name or a nested-member name.
- /// Top-level template instance id when known; null for a
- /// nested-struct member (which has no fetchable id in the decoded parent shape).
+ /// Template instance id when known — the top-level UDT's id, or a
+ /// nested-struct member's decoded from the parent
+ /// shape; null only when neither a seeded name-keyed shape nor an id can resolve the struct
+ /// (the caller then degrades to a single Variable rather than a broken fan-out).
/// Cancellation token for any live template read.
private async Task ResolveDiscoveredUdtShapeAsync(
string deviceHostAddress, string structName, uint? templateInstanceId, CancellationToken cancellationToken)
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/CipTemplateObjectDecoder.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/CipTemplateObjectDecoder.cs
index 1d0b1f3c..1bce8e91 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/CipTemplateObjectDecoder.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/CipTemplateObjectDecoder.cs
@@ -83,6 +83,8 @@ public static class CipTemplateObjectDecoder
var offset = (int)BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(blockOffset + 4));
var isStruct = (info & MemberInfoStructFlag) != 0;
+ // Scalar path only — Rockwell primitive type codes (0xC1-0xD0) fit a byte. Do NOT use for a
+ // struct member: a nested template id can exceed a byte (see nestedTemplateId below).
var typeCode = (byte)(info & MemberInfoTypeCodeMask);
var dataType = isStruct
? AbCipDataType.Structure