Skip to content

Deserialization and validation error handling #72

@JPHutchins

Description

@JPHutchins

Here's an example that highlights a problem. If the deserialization and validation fails, smpclient makes a final effort to deserialize as an Error. In this case, that makes the trace misleading. I suppose that if the Error deserialization fails, it can then point out that both deserialization attempts have failed. But maybe pydantic has some better approach?

related:

│                                                                                                                                                                                                                               │
│ ╭─────────────────────────────────────────────────────────────────────── locals ───────────────────────────────────────────────────────────────────────╮                                                                      │
│ │ data = {                                                                                                                                             │                                                                      │
│ │        │   'header': Header(                                                                                                                         │                                                                      │
│ │        │   │   op=<OP.READ_RSP: 1>,                                                                                                                  │                                                                      │
│ │        │   │   version=<Version.V2: 1>,                                                                                                              │                                                                      │
│ │        │   │   flags=<Flag.UNUSED: 0>,                                                                                                               │                                                                      │
│ │        │   │   length=38,                                                                                                                            │                                                                      │
│ │        │   │   group_id=10,                                                                                                                          │                                                                      │
│ │        │   │   sequence=1,                                                                                                                           │                                                                      │
│ │        │   │   command_id=3                                                                                                                          │                                                                      │
│ │        │   ),                                                                                                                                        │                                                                      │
│ │        │   'groups': [{'group': 100, 'name': '', 'handlers': 40}],                                                                                   │                                                                      │
│ │        │   'smp_data': bytearray(b'\t\x00\x00&\x00\n\x01\x03\xbffgroups\x9f\xbfegroup\x18ddname`hhandlers\x18(\xff\xff\xff')                         │                                                                      │
│ │        }                                                                                                                                             │                                                                      │
│ │ self = EnumManagementErrorV2()                                                                                                                       │                                                                      │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯                                                                      │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
ValidationError: 2 validation errors for EnumManagementErrorV2
err
  Field required [type=missing, input_value={'header': Header(op=<OP....lers\x18(\xff\xff\xff')}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing
groups
  Extra inputs are not permitted [type=extra_forbidden, input_value=[{'group': 100, 'name': '', 'handlers': 40}], input_type=list]
    For further information visit https://errors.pydantic.dev/2.11/v/extra_forbidden

Originally posted by @sgfeniex in #71 (comment)

Here's the code block causing this suppression and coercion:

try:
return request._Response.loads(frame) # type: ignore
except ValidationError:
pass
try:
return request._ErrorV1.loads(frame)
except ValidationError:
pass
try:
return request._ErrorV2.loads(frame)
except ValidationError:
error_message = (
f"Response could not by parsed as one of {request._Response}, "
f"{request._ErrorV1}, or {request._ErrorV2}. {header=} {frame=}"
)
logger.error(error_message)
raise ValidationError(error_message)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions