Skip to content

Conversation

@JaagupAverin
Copy link
Contributor

@JaagupAverin JaagupAverin commented Dec 1, 2025

Additionally fixes an issue where ValidationError(error) constructor would re-raise an exception because it requires additional arguments.

before:

Response could not by parsed as one of <class 'smp.os_management.TaskStatisticsReadResponse'>, <class 'smp.os_management.OSManagementErrorV1'>, or <class 'smp.os_management.OSManagementErrorV2'>. header=Header(op=<OP.READ_RSP: 1>, version=<Version.V2: 1>, flags=<Flag.UNUSED: 0>, length=213, group_id=0, sequence=2, command_id=2) frame=bytearray(b'\t\x00\x00\xd5\x00\x00\x02\x02\xbfetasks\xbfjmcumgr smp\xbfdprio\x03ctid\x00estate\x18\x80\xffjshell_uart\xbfdprio\x0ectid\x01estate\x18\x80\xffhsysworkq\xbfdprio ctid\x02estate\x02\xffkshell_dummy\xbfdprio\x0ectid\x03estate\x02\xffglogging\xbfdprio\x0ectid\x04estate\x02\xffdidle\xbfdprio\x0fctid\x05estate\x00\xffdmain\xbfdprio\x00ctid\x06estate\x04\xff\xff\xff')
2025-12-01 12:07:21 [TMP] [ERROR] execute_task_inner:62: Task '[connect_to_stm32] Connecting to STM32' raised an exception: ValidationError.__new__() missing 1 required positional argument: 'line_errors';
Traceback (most recent call last):
  File "/home/jaagup/projects/mtester/app/smpclient/smpclient/__init__.py", line 189, in request
    return request._ErrorV2.loads(frame)
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "/home/jaagup/projects/mtester/app/sbc/.venv/lib/python3.13/site-packages/smp/message.py", line 57, in loads
    message = cls(
        header=smpheader.Header.loads(data[: smpheader.Header.SIZE]),
        **cast(dict, cbor2.loads(data[smpheader.Header.SIZE :])),
        smp_data=data,
    )
  File "/home/jaagup/projects/mtester/app/sbc/.venv/lib/python3.13/site-packages/pydantic/main.py", line 250, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for OSManagementErrorV2
err
  Field required [type=missing, input_value={'header': Header(op=<OP....state\x04\xff\xff\xff')}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.12/v/missing
tasks
  Extra inputs are not permitted [type=extra_forbidden, input_value={'mcumgr smp': {'prio': 3..., 'tid': 6, 'state': 4}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/jaagup/projects/mtester/app/sbc/src/sbc/tester_lib/execution.py", line 59, in execute_task_inner
    return await task.fn(self.tester)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jaagup/projects/mtester/app/sbc/./src/sbc/tester.py", line 51, in connect_to_stm32
    return await self.stm32_smp.connect()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jaagup/projects/mtester/app/sbc/src/sbc/tester_lib/serial_smp.py", line 57, in connect
    return await self.ping_pong()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jaagup/projects/mtester/app/sbc/src/sbc/tester_lib/serial_smp.py", line 46, in ping_pong
    task_stats = await self.client.request(TaskStatisticsRead())
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jaagup/projects/mtester/app/smpclient/smpclient/__init__.py", line 196, in request
    raise ValidationError(error_message)
          ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
TypeError: ValidationError.__new__() missing 1 required positional argument: 'line_errors'

after:

Frame could not be parsed as any of:
	['TaskStatisticsReadResponse', 'OSManagementErrorV1', 'OSManagementErrorV2']
Header:
	Header(op=<OP.READ_RSP: 1>, version=<Version.V2: 1>, flags=<Flag.UNUSED: 0>, length=213, group_id=0, sequence=2, command_id=2)
Frame:
	0000  09 00 00 d5 00 00 02 02 bf 65 74 61 73 6b 73 bf  .........etasks.
	0010  6a 6d 63 75 6d 67 72 20 73 6d 70 bf 64 70 72 69  jmcumgr smp.dpri
	0020  6f 03 63 74 69 64 00 65 73 74 61 74 65 18 80 ff  o.ctid.estate...
	0030  6a 73 68 65 6c 6c 5f 75 61 72 74 bf 64 70 72 69  jshell_uart.dpri
	0040  6f 0e 63 74 69 64 01 65 73 74 61 74 65 18 80 ff  o.ctid.estate...
	0050  68 73 79 73 77 6f 72 6b 71 bf 64 70 72 69 6f 20  hsysworkq.dprio
	0060  63 74 69 64 02 65 73 74 61 74 65 02 ff 6b 73 68  ctid.estate..ksh
	0070  65 6c 6c 5f 64 75 6d 6d 79 bf 64 70 72 69 6f 0e  ell_dummy.dprio.
	0080  63 74 69 64 03 65 73 74 61 74 65 02 ff 67 6c 6f  ctid.estate..glo
	0090  67 67 69 6e 67 bf 64 70 72 69 6f 0e 63 74 69 64  gging.dprio.ctid
	00a0  04 65 73 74 61 74 65 02 ff 64 69 64 6c 65 bf 64  .estate..didle.d
	00b0  70 72 69 6f 0f 63 74 69 64 05 65 73 74 61 74 65  prio.ctid.estate
	00c0  00 ff 64 6d 61 69 6e bf 64 70 72 69 6f 00 63 74  ..dmain.dprio.ct
	00d0  69 64 06 65 73 74 61 74 65 04 ff ff ff           id.estate....
Errors:
	Could not be parsed as TaskStatisticsReadResponse because 42 errors:
		[missing] Field required: tasks.mcumgr smp.stkuse; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.mcumgr smp.stksize; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.mcumgr smp.cswcnt; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.mcumgr smp.runtime; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.mcumgr smp.last_checkin; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.mcumgr smp.next_checkin; input: {'prio': 3, 'tid': 0, 'state': 128})
		[missing] Field required: tasks.shell_uart.stkuse; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.shell_uart.stksize; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.shell_uart.cswcnt; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.shell_uart.runtime; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.shell_uart.last_checkin; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.shell_uart.next_checkin; input: {'prio': 14, 'tid': 1, 'state': 128})
		[missing] Field required: tasks.sysworkq.stkuse; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.sysworkq.stksize; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.sysworkq.cswcnt; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.sysworkq.runtime; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.sysworkq.last_checkin; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.sysworkq.next_checkin; input: {'prio': -1, 'tid': 2, 'state': 2})
		[missing] Field required: tasks.shell_dummy.stkuse; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.shell_dummy.stksize; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.shell_dummy.cswcnt; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.shell_dummy.runtime; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.shell_dummy.last_checkin; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.shell_dummy.next_checkin; input: {'prio': 14, 'tid': 3, 'state': 2})
		[missing] Field required: tasks.logging.stkuse; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.logging.stksize; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.logging.cswcnt; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.logging.runtime; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.logging.last_checkin; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.logging.next_checkin; input: {'prio': 14, 'tid': 4, 'state': 2})
		[missing] Field required: tasks.idle.stkuse; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.idle.stksize; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.idle.cswcnt; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.idle.runtime; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.idle.last_checkin; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.idle.next_checkin; input: {'prio': 15, 'tid': 5, 'state': 0})
		[missing] Field required: tasks.main.stkuse; input: {'prio': 0, 'tid': 6, 'state': 4})
		[missing] Field required: tasks.main.stksize; input: {'prio': 0, 'tid': 6, 'state': 4})
		[missing] Field required: tasks.main.cswcnt; input: {'prio': 0, 'tid': 6, 'state': 4})
		[missing] Field required: tasks.main.runtime; input: {'prio': 0, 'tid': 6, 'state': 4})
		[missing] Field required: tasks.main.last_checkin; input: {'prio': 0, 'tid': 6, 'state': 4})
		[missing] Field required: tasks.main.next_checkin; input: {'prio': 0, 'tid': 6, 'state': 4})
	Could not be parsed as OSManagementErrorV1 because 2 errors:
		[missing] Field required: rc; input: {'header': Header(op=<OP.READ_RSP: 1>, version=<Version.V2: 1>, flags=<Flag.UNUSED: 0>, length=213, group_id=0, sequence=2, command_id=2), 'tasks': {'mcumgr smp': {'prio': 3, 'tid': 0, 'state': 128}, 'shell_uart': {'prio': 14, 'tid': 1, 'state': 128}, 'sysworkq': {'prio': -1, 'tid': 2, 'state': 2}, 'shell_dummy': {'prio': 14, 'tid': 3, 'state': 2}, 'logging': {'prio': 14, 'tid': 4, 'state': 2}, 'idle': {'prio': 15, 'tid': 5, 'state': 0}, 'main': {'prio': 0, 'tid': 6, 'state': 4}}, 'smp_data': bytearray(b'\t\x00\x00\xd5\x00\x00\x02\x02\xbfetasks\xbfjmcumgr smp\xbfdprio\x03ctid\x00estate\x18\x80\xffjshell_uart\xbfdprio\x0ectid\x01estate\x18\x80\xffhsysworkq\xbfdprio ctid\x02estate\x02\xffkshell_dummy\xbfdprio\x0ectid\x03estate\x02\xffglogging\xbfdprio\x0ectid\x04estate\x02\xffdidle\xbfdprio\x0fctid\x05estate\x00\xffdmain\xbfdprio\x00ctid\x06estate\x04\xff\xff\xff')})
		[extra_forbidden] Extra inputs are not permitted: tasks; input: {'mcumgr smp': {'prio': 3, 'tid': 0, 'state': 128}, 'shell_uart': {'prio': 14, 'tid': 1, 'state': 128}, 'sysworkq': {'prio': -1, 'tid': 2, 'state': 2}, 'shell_dummy': {'prio': 14, 'tid': 3, 'state': 2}, 'logging': {'prio': 14, 'tid': 4, 'state': 2}, 'idle': {'prio': 15, 'tid': 5, 'state': 0}, 'main': {'prio': 0, 'tid': 6, 'state': 4}})
	Could not be parsed as OSManagementErrorV2 because 2 errors:
		[missing] Field required: err; input: {'header': Header(op=<OP.READ_RSP: 1>, version=<Version.V2: 1>, flags=<Flag.UNUSED: 0>, length=213, group_id=0, sequence=2, command_id=2), 'tasks': {'mcumgr smp': {'prio': 3, 'tid': 0, 'state': 128}, 'shell_uart': {'prio': 14, 'tid': 1, 'state': 128}, 'sysworkq': {'prio': -1, 'tid': 2, 'state': 2}, 'shell_dummy': {'prio': 14, 'tid': 3, 'state': 2}, 'logging': {'prio': 14, 'tid': 4, 'state': 2}, 'idle': {'prio': 15, 'tid': 5, 'state': 0}, 'main': {'prio': 0, 'tid': 6, 'state': 4}}, 'smp_data': bytearray(b'\t\x00\x00\xd5\x00\x00\x02\x02\xbfetasks\xbfjmcumgr smp\xbfdprio\x03ctid\x00estate\x18\x80\xffjshell_uart\xbfdprio\x0ectid\x01estate\x18\x80\xffhsysworkq\xbfdprio ctid\x02estate\x02\xffkshell_dummy\xbfdprio\x0ectid\x03estate\x02\xffglogging\xbfdprio\x0ectid\x04estate\x02\xffdidle\xbfdprio\x0fctid\x05estate\x00\xffdmain\xbfdprio\x00ctid\x06estate\x04\xff\xff\xff')})
		[extra_forbidden] Extra inputs are not permitted: tasks; input: {'mcumgr smp': {'prio': 3, 'tid': 0, 'state': 128}, 'shell_uart': {'prio': 14, 'tid': 1, 'state': 128}, 'sysworkq': {'prio': -1, 'tid': 2, 'state': 2}, 'shell_dummy': {'prio': 14, 'tid': 3, 'state': 2}, 'logging': {'prio': 14, 'tid': 4, 'state': 2}, 'idle': {'prio': 15, 'tid': 5, 'state': 0}, 'main': {'prio': 0, 'tid': 6, 'state': 4}})

@JaagupAverin JaagupAverin force-pushed the error_handling branch 7 times, most recently from 9af793e to a968599 Compare December 1, 2025 10:41
@JaagupAverin
Copy link
Contributor Author

JaagupAverin commented Dec 1, 2025

I'm not sure what the lint issue is. Cannot reproduce locally. Enabling --diff in the job should make it easier to fix.

Additionally fixes an issue where `ValidationError(error)` constructor would re-raise an exception because it requires additional arguments.
@JPHutchins
Copy link
Collaborator

This should fix #72, please review. We may benefit from pydantic's "left to right" coercion/validation.

@JPHutchins
Copy link
Collaborator

Looking good, thank you! I like dumping the bytes, and it made me realize that we could deserialize the CBOR and pretty print it as well, if possible.

@JPHutchins
Copy link
Collaborator

@JaagupAverin I think that my PR to your PR should resolve the linting issues, please take a look when possible: JaagupAverin#1

@JPHutchins
Copy link
Collaborator

@JaagupAverin I think that my PR to your PR should resolve the linting issues, please take a look when possible: JaagupAverin#1

Running it here: #91

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.

2 participants