Summary
OatEvent.detail is typed as Union[EndpointActivity, EmailActivity], covering only 2 of the 7 values declared in OatEntityType. Any OAT event whose entity type is cloudtrail, messaging, network,
ics, or container fails pydantic validation, which raises ValidationError from oat.consume() (and oat.list()) and aborts pagination mid-stream. The SDK currently has no fallback path for these
events.
Affected version
pytmv1 == 0.11.0 (also present on main, latest commit at the time of writing).
Root cause
-
src/pytmv1/model/enum.py:157-164 — OatEntityType has 7 members:
class OatEntityType(str, Enum):
ENDPOINT = "endpoint"
MAILBOX = "mailbox"
CLOUDTRAIL = "cloudtrail"
MESSAGING = "messaging"
NETWORK = "network"
ICS = "ics"
CONTAINER = "container"
-
src/pytmv1/model/common.py:625 — OatEvent.detail only models 2 of those 7:
class OatEvent(BaseConsumable):
...
entity_type: OatEntityType
...
detail: Union[EndpointActivity, EmailActivity]
-
EndpointActivity requires endpoint_guid (endpointGuid); EmailActivity requires msg_uuid (msgUuid). For an event with entity_type ∈ {cloudtrail, messaging, network, ics, container}, neither
branch of the union validates, so pydantic raises ValidationError and the consumer never sees the event — nor any later events on that page or subsequent pages.
Observed behavior
Reproduced against a production tenant via client.oat.consume(consumer, start_time=..., end_time=..., top=50):
- Pages 1–2: ~101 events consumed successfully.
- Page 3: parse fails on 2 of 50 events whose
detail payload looks like {"act": ["log"], ...} (no endpointGuid, no msgUuid) — consistent with a non-endpoint/non-email entity type. The exception
aborts the entire consume() call.
Prevalence in our sample was ~4% (2/50 in a single page), so any tenant running cloudtrail / network / container detection rules will hit this in practice. The bug is not a transient HTTP / timeout issue —
increasing read_timeout or shrinking page_size does not help, since the failure is deterministic on payload shape.
Impact
oat.consume() and oat.list() are effectively unusable on real tenants that use OAT detections beyond endpoint/email.
- There is no in-SDK workaround:
consume() parses internally, so the caller cannot intercept the raw response before validation. Falling back to a manual HTTP client defeats the purpose of using the SDK
for OAT.
- For comparison,
alert.consume() is unaffected because SaeAlert / TiAlert cover the full set of providers returned by the Workbench API.
Proposed fix
Two options, in increasing scope:
-
Minimal — add a permissive fallback. Change
detail: Union[EndpointActivity, EmailActivity]
to
detail: Union[EndpointActivity, EmailActivity, Dict[str, Any]]
so that endpoint and email events stay strongly typed while non-modeled types are surfaced as a raw dict and the consume loop keeps making forward progress. ~1 line, fully backward-compatible.
-
Complete — model the remaining 5 entity types. Add CloudTrailActivity, MessagingActivity, NetworkActivity, IcsActivity, ContainerActivity (or whatever names match the public API doc) and
discriminate on entity_type. Larger PR, requires the API schema for each, but gives users typed access to every event.
Happy to send a PR for option 1 if that path is acceptable; option 2 would need guidance on the expected schemas (or pointers to the API reference for these entity types).
Temporary workaround for affected users
If the API supports server-side filtering on entityType, restricting the query to endpoint and mailbox keeps consume() working at the cost of dropping the other 5 types entirely. This is a stop-gap,
not a fix — the dropped events are exactly the ones some tenants care most about (cloudtrail, network).
Environment
pytmv1 == 0.11.0
- Python 3.10
- Reproduced against a production Vision One tenant; cannot share payloads publicly, but happy to share a redacted example privately if useful.
Summary
OatEvent.detailis typed asUnion[EndpointActivity, EmailActivity], covering only 2 of the 7 values declared inOatEntityType. Any OAT event whose entity type iscloudtrail,messaging,network,ics, orcontainerfails pydantic validation, which raisesValidationErrorfromoat.consume()(andoat.list()) and aborts pagination mid-stream. The SDK currently has no fallback path for theseevents.
Affected version
pytmv1 == 0.11.0(also present onmain, latest commit at the time of writing).Root cause
src/pytmv1/model/enum.py:157-164—OatEntityTypehas 7 members:src/pytmv1/model/common.py:625—OatEvent.detailonly models 2 of those 7:EndpointActivityrequiresendpoint_guid(endpointGuid);EmailActivityrequiresmsg_uuid(msgUuid). For an event withentity_type ∈ {cloudtrail, messaging, network, ics, container}, neitherbranch of the union validates, so pydantic raises
ValidationErrorand the consumer never sees the event — nor any later events on that page or subsequent pages.Observed behavior
Reproduced against a production tenant via
client.oat.consume(consumer, start_time=..., end_time=..., top=50):detailpayload looks like{"act": ["log"], ...}(noendpointGuid, nomsgUuid) — consistent with a non-endpoint/non-email entity type. The exceptionaborts the entire
consume()call.Prevalence in our sample was ~4% (2/50 in a single page), so any tenant running cloudtrail / network / container detection rules will hit this in practice. The bug is not a transient HTTP / timeout issue —
increasing
read_timeoutor shrinkingpage_sizedoes not help, since the failure is deterministic on payload shape.Impact
oat.consume()andoat.list()are effectively unusable on real tenants that use OAT detections beyond endpoint/email.consume()parses internally, so the caller cannot intercept the raw response before validation. Falling back to a manual HTTP client defeats the purpose of using the SDKfor OAT.
alert.consume()is unaffected becauseSaeAlert/TiAlertcover the full set of providers returned by the Workbench API.Proposed fix
Two options, in increasing scope:
Minimal — add a permissive fallback. Change
to
so that endpoint and email events stay strongly typed while non-modeled types are surfaced as a raw dict and the consume loop keeps making forward progress. ~1 line, fully backward-compatible.
Complete — model the remaining 5 entity types. Add
CloudTrailActivity,MessagingActivity,NetworkActivity,IcsActivity,ContainerActivity(or whatever names match the public API doc) anddiscriminate on
entity_type. Larger PR, requires the API schema for each, but gives users typed access to every event.Happy to send a PR for option 1 if that path is acceptable; option 2 would need guidance on the expected schemas (or pointers to the API reference for these entity types).
Temporary workaround for affected users
If the API supports server-side filtering on
entityType, restricting the query toendpointandmailboxkeepsconsume()working at the cost of dropping the other 5 types entirely. This is a stop-gap,not a fix — the dropped events are exactly the ones some tenants care most about (cloudtrail, network).
Environment
pytmv1 == 0.11.0