Errors
Nothing in pycosmicsdk returns a status code. Every failure is an exception,
and every exception derives from CosmicError, which
carries a status_code and an optional operation label naming the call site.
Exception
└── CosmicError
├── ValidationError arguments rejected before any native call
├── CapabilityError protocol the device does not have
├── DeviceNotFoundError no adapter matched the open criteria
├── UnsupportedFirmwareError firmware too old for this package to speak to
├── DisconnectError device gone mid-operation
├── AdapterNotAnsweringError handle live, adapter stopped replying
├── CosmicTimeoutError also a builtins.TimeoutError
├── ProtocolError protocol-level, not firmware state
├── CosmicSDKLibraryNotFound bundled shared library not located
├── CosmicSDKVersionError loaded library reports an incompatible version
├── CosmicInternalError a violated SDK invariant -- a bug in this SDK
└── FirmwareError reported by device firmware, via a status code
├── SystemFirmwareError 0x01xx band (voltage, reset, ...)
├── I2cError 0x02xx band
├── I2cDutyCycleRefusedError set_duty_cycle's FW_INVALID_PARAMETER, with the settings kept
├── SpiError 0x03xx band — SPI target mode only, see below
├── UartError 0x04xx band
├── GpioError 0x05xx band
├── I3cError 0x06xx band
└── DebugError 0x1Dxx/0x1Exx — a DebugProbe's target flash and debug session
└── TargetFlashVerifyError TargetFlash.write's readback mismatch, with the address
The bands are upstream’s own: a firmware result code carries its GROUP_* in
the high byte (submodules/CosmicSDK/include/definitions.h). Generic firmware
codes (0x0001–0x000E) sit below every band, and anything from 0x0700 up
other than 0x1Exx sits above them; both fall through to
FirmwareError itself. 0x1Exx are the debug probe’s
host-side debug codes (DebugProbeDebugCodes.h), raised as
DebugError with the code’s name in the message.
Which one to catch
CosmicError is the blanket catch — every example script uses it at the
top level.
FirmwareError is the right granularity for “the device rejected this”.
The per-protocol subclasses are narrower than their names suggest, because a
status code lands in a protocol band only if firmware put it there:
A rejected configuration is normally the generic
FW_INVALID_PARAMETER, and an uninitialised interfaceFW_INTERFACE_NOT_INITIALIZED— both below every band, so both plainFirmwareError.A rejected voltage is in the 0x01xx system band, so
SystemFirmwareError, whichever interface asked for it.UartErrorcovers exactly one upstream code,FW_UART_IN_VCOM_PORT_MODE.I2cError’s band, by contrast, is well populated — address NACK, arbitration lost, SCL held low.
ValidationError is raised Python-side, before any USB traffic: bad
argument ranges and field widths, over-the-maximum payloads, the GPIO direction
guard, the HDR-DDR length constraints, I3C target memory bounds.
CapabilityError is also raised Python-side, before any USB traffic — see
the section below, which is the one thing on this page most likely to catch a
reader out.
CosmicTimeoutError multi-inherits TimeoutError, so
except TimeoutError: catches it. The Cosmic prefix keeps it from shadowing
the built-in after a from pycosmicsdk import *.
AdapterNotAnsweringError is this SDK refusing to send, after three
consecutive timeouts on one handle with nothing answering in between. It is
not a TimeoutError — see the section below for why that is deliberate.
examples/blocking_api/i2c/error_handling.py provokes one failure per layer
and prints which exception each produces.
Capability refusals arrive before the wire
CapabilityError means this adapter cannot do this, and
nothing was sent. It covers two kinds of limit — a protocol the model does not
have at all (Device.i3c() on a Pulsar, an
I2cBus the model does not carry), and a narrower limit
that a guard checks in front of an otherwise-available call:
Refused call |
Because |
|---|---|
the hardware revision cannot reconfigure a live I2C interface |
|
|
this model’s firmware has no table row for the requested |
the hardware revision cannot arm an interrupt on that pin |
|
Any of the three protocols’ |
the device or the host declined to negotiate the bulk transport. I2C and I3C refuse exactly as SPI does, each through its own guard: |
|
this hardware revision cannot receive an incoming chip select — a rev-B Supernova, and only that one revision |
|
the target’s memory arena travels only over the bulk plane and has no HID route, so an unreachable plane is refused at arming rather than two calls later. Waived with |
this model fits no transceiver for the requested |
Two consequences, and both have bitten this repository’s own examples:
It is not a FirmwareError. CapabilityError derives straight from
CosmicError, so a handler written as
except FirmwareError — the natural shape for “the device rejected this” —
does not catch it. Give it its own clause.
Do not filter it on status_code. Because nothing reached the device,
there is no firmware rejection code to carry; the guards raise
StatusCode.SDK_ERROR_WRONG_REQUEST. A handler that catches CapabilityError
and then re-raises unless the code is on some allow-list re-raises every
capability refusal. That is why the pattern below puts the two on separate
clauses and filters only the firmware one:
from pycosmicsdk import CapabilityError, FirmwareError, StatusCode
try:
i2c.configure(frequency_hz=400_000, pull_up=I2cPullUp.OHM_330)
except CapabilityError:
# Refused before the wire, so this is by definition the recoverable case:
# the bus is still running exactly as initialize() left it. No status
# filter -- the code is SDK_ERROR_WRONG_REQUEST, not a firmware code.
settings_applied = False
except FirmwareError as exc:
# Here the code *is* what distinguishes a rejected config from a fault.
if exc.status_code is not StatusCode.FW_FEATURE_NOT_SUPPORTED_BY_HARDWARE:
raise
settings_applied = False
examples/helpers.py (i2c_bring_up, i2c_set_pull_up, and their asyncio
twins) is the bench-verified version of exactly this, and every I2C example
goes through it.
Note
StatusCode.SDK_ERROR_WRONG_REQUEST is the SDK’s generic “this request was
never sent” code and it is carried by four different classes: a
ValidationError (a bad argument), a
CapabilityError (a model or revision limit), a
ProtocolError (a request CosmicSDK itself rejected as
malformed, and that this SDK did not pre-empt — a sub-address longer than four
bytes is the live case) and
CosmicInternalError. The
class carries the meaning; the code does not. Branch on the class.
Upstream also rejects an I3C PID that is not six bytes and an out-of-range
target-memory address with the same code, but neither reaches it: this SDK
checks both Python-side first, so they arrive as
ValidationError.
Better than catching: ask first. DeviceCapabilities
answers all five limits with no USB traffic —
capabilities.i2c_runtime_reconfigurable,
capabilities.supports_i2c_pull_up(pull_up),
capabilities.supports_gpio_interrupt(pin),
capabilities.spi_target_capable and
capabilities.supports_spi_bulk() / capabilities.supports_i3c_bulk(). See
Capabilities you can ask about in advance.
Refusals fail open
A CapabilityError tells you one of three things, and it is worth knowing
which before you go looking for a cable fault:
You have the wrong device. A protocol or bus the model does not carry —
Device.i3c() on a Pulsar, I2cBus.B on a
Supernova, a CdcMode whose transceiver this board does not fit, an
I2cPullUp with no row in this model’s firmware table. No
revision of that model changes the answer and no argument avoids it; the fix is
a different adapter, or a different value.
You have the wrong revision. A limit of the board, not the model:
reconfiguring a live I2C interface, or arming a GPIO interrupt on the one pin
whose revision cannot. The same model at another revision does it fine. The
message names the revision it read, so SUPERNOVA HW-B in the text is the
thing to check.
You have the wrong expectation. SpiTransport.BULK is a demand, not a
preference. SpiTransport.AUTO asks for the best available transport and never
raises this.
Important
An unknown answer means “attempt it”, never “refuse it”. This SDK carries the model and revision tables in Python because CosmicSDK exposes no capabilities query, and a table can be out of date in two directions. Refusing an operation that would have worked is the worse failure — a user cannot work around it — so every guard is arranged so that ignorance falls through to the device:
A model firmware does not apply the limit to is not gated at all. The live-reconfigure check exists only in the Supernova firmware build, so
i2c_runtime_reconfigurableisTrueon every Pulsar whatever itshw_versionsays, and the guard in front ofconfigurenever fires there.A
DeviceModelthis release has never been taught reaches no guard either, and theCdcModecheck has no entry for it and passes every mode through.An
hw_versionstring that does not parse as a revision — an empty field, a firmware that starts reporting a semver, a shape nobody anticipated — is attempted rather than refused. The one gate written as an equality (i2c_runtime_reconfigurable, mirroring firmware’s ownhwVersion != 'C') routes both that case and an ungated model around the equality rather than through it, because inverting a table is exactly where fail-open gets lost.The GPIO interrupt table lists the pin/revision pairs known to be broken, so a pair absent from it is armed. That is why it holds a single cell — Supernova Rev B,
PIN_3— rather than an enumeration of what works.
The two pull-up tables are the exception, and read the other way round: they enumerate the values firmware accepts, per model, so a value absent from one is refused here. They can be that strict because firmware’s own table is a closed per-model set — a value it does not name has no resistance to program, so there is nothing to fail open to.
The cost of failing open is that a limit this SDK has not been taught still
reaches the device and comes back as a firmware code — typically
FW_FEATURE_NOT_SUPPORTED_BY_HARDWARE or FW_INVALID_PARAMETER, as a plain
FirmwareError. That is the deliberate trade: a loud,
late, correctly-attributed failure in preference to an early refusal that might
have been wrong.
Whole protocols are the one thing that does not fail open, and it is worth
knowing before you meet it. protocols and i2c_buses are enumerated
positively per known model, so an adapter whose model this release does not
recognise reports both as empty and every interface accessor —
Device.i2c(),
spi, uart,
gpio, i3c —
raises CapabilityError. The handle still opens and
info still reports what the adapter said about
itself, which is the point: you can identify an unrecognised unit and file a
report, but you cannot drive it. If a supported adapter ever lands here, the
model marker is what to look at first — it comes from the USB product id,
not from a product string.
Note
SpiError has exactly one source: SPI target mode.
Both status translators map the 0x03xx firmware band to it, and all seven codes
in that band are target-mode ones — FW_SPI_ROLE_CONFLICT,
FW_SPI_TARGET_STAGING_UNAVAILABLE, FW_SPI_TARGET_SURPASSED_MEMORY_SIZE,
FW_SPI_TARGET_PAYLOAD_TRANSFER_FAILED, FW_SPI_TARGET_WRONG_MODEL,
FW_SPI_TARGET_INVALID_CONFIG and FW_SPI_TARGET_REGION_TABLE_FULL. So a
program that never calls Device.spi_target() will never see one, and controller-side SPI
failures still arrive as FirmwareError (generic codes)
or SystemFirmwareError (voltage). The SPI
guide has the full per-failure table.
What this note said until 2026-09-05. It was a warning headed “SpiError
is unreachable today — do not rely on catching it”, and argued that
CosmicSDK’s definitions.h declared no FW_SPI_* status code at all, so the
band was empty upstream and nothing reached the class. That was a true
statement about an older submodule pin. The pinned definitions.h now declares
the seven codes above and _status_maps.py maps every one of them to
SpiError, so the advice it gave — catch FirmwareError instead — is now
right for controller code only.
Note
CosmicInternalError — a violated internal invariant, an
SDK bug rather than user error — is re-exported at package level:
from pycosmicsdk import CosmicInternalError
It was previously the only one of the eighteen names in pycosmicsdk.errors
that the top-level package did not re-export, which for a raisable class was an
oversight rather than a decision. It is now in pycosmicsdk.__all__, so
from pycosmicsdk import * brings it in and it needs no import from the
submodule.
It has one raise site — transaction-id exhaustion in the async engine, which
can only happen if the SDK leaks pending entries — so an application that
catches CosmicError already covers it, and one that
catches it specifically is reporting a bug in this SDK rather than handling a
device condition.
AdapterNotAnsweringError means this SDK stopped sending
AdapterNotAnsweringError is the only class on this page
that is about the SDK’s own conduct rather than about your arguments, your
device’s answer, or a message that did not parse. It says: three commands in
a row on this handle timed out with no reply in between, so nothing more will
be sent on it. No command was issued for the call that raised it.
It exists because continuing is not neutral. Backlog ticket 72 records a bench
adapter driven through several handle cycles of timed-out commands, each one
absorbed and followed by another, which ended up enumerating over USB-HID while
refusing every open for more than 270 seconds — a
state a physical unplug/replug was the only way out of, because no handle could
be opened to send anything on, a reset included. On the firmware side
the queues that back up are a handful of entries deep and every enqueue waits
forever rather than failing, so a run of unanswered commands is what walks a
device into it.
Three, not one. A single timeout is a normal, documented thing on real
hardware and its shape is untouched here: it is still a
CosmicTimeoutError, and so are the second and third. The
guard changes nothing about a command it issued — only the command after the
third refusal-free timeout is declined. Anything that answers, including a
firmware rejection, resets the count: an empty I2C bus reporting
FW_I2C_BUS_WITH_NO_TARGETS_CONNECTED in five milliseconds is a healthy
adapter.
Both surfaces, one rule. Device and
AsyncDevice count identically — same limit of three,
same definition of an answer, same refusal — because they share the count. An
await reaches a wedged adapter exactly as a call does. Two asyncio details
follow from commands being able to overlap there: concurrent awaits that all
time out each count, so a gather of four unanswered commands trips the guard
as surely as four sequential ones; and a command you cancel yourself counts for
nothing in either direction, because a cancellation says nothing about whether
the adapter would have replied.
What is exempt. Operations that cross no wire —
Device.get_version and
Device.subscribe, with their async
twins — are neither refused on a tripped handle nor treated as answers on a
live one, so a diagnostic loop cannot silently disarm the guard and a
post-mortem can still read the handle, and start watching it, after it trips.
Registering a subscription installs a host-side callback and sends nothing.
Everything that can reach the wire is refused once the count is reached,
including calls that would have sent nothing this time: the guard is about the
handle, not the command.
Only an issued command counts, in either direction. A call that takes the
handle and sends nothing is not an answer, and does not reset the run. That
covers the calls a poll loop is built out of —
I2cTarget.drain_events,
wait_for_event,
Uart.receive, the max_transfer_bytes and
transport reads — none of which reach the adapter at all. Interleaving one
between every command therefore has no effect on the count, which is what makes
the guard reach the callers most likely to need it. Their own timeouts are not
evidence either: Uart.receive timing out
means the peer sent nothing, not that the adapter stopped answering. This is
separate from the exemption above, which is about the refusal: once the count
is reached these calls are declined like any other.
Catching it. It derives from CosmicError and
deliberately from neither CosmicTimeoutError nor
FirmwareError. except TimeoutError: will not catch
it, which is the point: retrying on a timeout is the behaviour it exists to
interrupt, and a handler that swallowed it as one more timeout would keep the
loop running.
Recovering. The count is per handle. A new
open or
open starts a fresh one, and that the count
is not keyed to the adapter’s serial is deliberate: such a latch would outlive
the physical replug that fixes the worst case. Whether the adapter answers on
that new handle is a property of the adapter.
If it does answer on the new handle, the SDK has one more lever, and this
page said until 2026-09-05 that it had none: reboot
with RUNTIME. A runtime reset clears latched
firmware interface state — measured on a Pulsar, it cleared a persistent I2C
controller role on both buses — and the adapter was openable again 0.825 s
later. That is one observation on one adapter, so poll
list_devices rather than sleeping a fixed interval, and
note that it does not clear the bus VTARG setpoint.
A replug is what remains for the case where the reset is not dispatched at
all. Ticket 72’s state is exactly that: a stranded protocol manager blocks the
USB receive decoder, after which no command of any group is decoded, SYS
included — so the reset request never reaches the code that would execute it.
Do not read “a reset clears latched state” as “a reset recovers a wedge”; that
has not been tested, and the one relevant observation says the opposite for a
device in that state.
Warning
This guard cannot un-wedge a device, and it is not a proof against wedging one. It stops this SDK adding to a run of unanswered commands; it arrives too late for a device that has already stopped answering, and firmware has at least one path to a mute HID interface that a single abandoned transfer can reach, which no host-side count can prevent. Treat it as a diagnosis worth acting on, not as a guarantee.
ProtocolError means the answer did not parse
ProtocolError is the one class on this page that is
about neither your arguments nor a firmware rejection. It says: the SDK and
the device did not agree on the shape of a message. Nothing in it is a firmware
status code, so there is no FW_* value to branch on and no bus condition to
retry through.
Both status translators map exactly three SDK-level codes to it —
SDK_ERROR_WRONG_REQUEST, SDK_ERROR_WRONG_RESPONSE and
SDK_ERROR_UNEXPECTED_RESPONSE — which is two distinct situations:
The request was never formed. CosmicSDK refused to build the frame,
so nothing was sent. The live case is a subaddress longer than four bytes on
any I2C or I3C read/write; each of those methods documents
ProtocolError in its Raises: block for exactly that.
The response could not be decoded. The adapter answered, and the reply was
too short to carry the fields it should have, or carried a value this release
does not name. get_usb_config raises it for a
CdcMode newer than this SDK;
get_analog_measurements raises it for a
reply that does not carry all six rails; the GPIO logic-level read, the CCC
decoders and the length-prefixed protocol reads each have the same bounds
check in front of them, because upstream reads several of these blocks by
reinterpret_cast with no length check at all.
Important
Firmware that predates a command reports itself as a
ProtocolError, not as an unsupported-command
FirmwareError. A firmware error response carries the
six-byte generic header and nothing else, so from the decoder’s point of view
it is simply a reply too short to parse. This is documented behaviour on
get_usb_config and
get_analog_measurements, whose docstrings
both say so — a ProtocolError from either on an older adapter most likely
means update the firmware, not retry.
So a handler for it does not recover; it reports. What is worth printing alongside is which versions were in play, since a mismatch between them is the usual cause:
import pycosmicsdk
from pycosmicsdk import Device, ProtocolError
with Device.open() as dev:
try:
config = dev.get_usb_config()
except ProtocolError as exc:
print(f"could not read the USB configuration: {exc}")
print(f" pycosmicsdk : {pycosmicsdk.__version__}")
print(f" CosmicSDK library: {dev.get_version()}")
print(f" adapter firmware : {dev.info.fw_version}")
else:
print(config)
The two loader errors nothing raises yet
CosmicSDKLibraryNotFound and
CosmicSDKVersionError are in the hierarchy above and in
pycosmicsdk.__all__, and no code path in this SDK raises either of them.
They are wired for the same reason SpiError is: the
class should already exist if the condition ever becomes reachable.
The condition is not reachable today because there is nothing to load. The
native library is statically linked into the compiled extension, so there
is no shared library to locate on disk (Device.open() takes no lib_path,
and there is no COSMICSDK_LIB_PATH environment variable) and no separately
installed version to check against. Both names are carried over from a
superseded ctypes design in which the library was found and version-checked
at run time.
What you can actually meet at import time is Python’s own ImportError, from
import pycosmicsdk failing to load the extension module — a broken or
mismatched wheel, or a source checkout that was never built. That is not a
CosmicError and no except CosmicError will see it.
Do not write a handler for these two expecting to catch a bad install.
Reference
- class pycosmicsdk.CosmicError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
ExceptionBase class for every error raised by
pycosmicsdk.eq=Falseensures that exceptions are only considered equal if they are the same object, which is the intended behavior for exceptions.status_codeis normally aStatusCodeenum value but may also be a plainintfor firmware codes the SDK has not yet mapped — the typed formatter falls back to a hex digest in that case so the error string remains human-readable.- status_code
The originating status code; a
StatusCodeenum value, or a plainintfor firmware codes the SDK has not yet mapped.
- class pycosmicsdk.ValidationError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when arguments are rejected before any CosmicSDK call is made.
- class pycosmicsdk.CapabilityError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when a call targets a protocol the device does not support.
- class pycosmicsdk.DeviceNotFoundError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when no device matching the connect criteria is enumerated.
- class pycosmicsdk.UnsupportedFirmwareError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when the adapter runs firmware this package cannot speak to.
CosmicSDK frames every request with the firmware 4.0 USB transfer protocol header; firmware 3.x writes raw commands with none, so nothing sent reaches a 3.x adapter and the first command spends the whole timeout. That timeout is what a caller used to receive, and it reads as a broken adapter rather than as an adapter this package cannot drive.
Deliberately not a
CosmicTimeoutError: a caller looping on a timeout retries, and against this adapter that loop never ends. Nothing about the device will change until its firmware does.
- class pycosmicsdk.DisconnectError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when the device is no longer reachable mid-operation.
- class pycosmicsdk.AdapterNotAnsweringError(status_code: StatusCode | int, operation: str | None = None, message: str = '', consecutive_timeouts: int = 0, waited_s: float = 0.0)
Bases:
CosmicErrorRaised when this handle refuses to issue another command, and why.
Not a device answer and not a timeout: it is this SDK declining to send anything more on a handle whose last several commands all timed out. It derives from
CosmicErrorand deliberately not fromCosmicTimeoutError, because the behaviour it exists to stop is a caller looping onexcept TimeoutError:and retrying. The message names the recovery steps, in the order worth trying them.
- class pycosmicsdk.CosmicTimeoutError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicError,TimeoutErrorRaised on
SDK_ERROR_TIMEOUT.Multi-inherits
builtins.TimeoutErrorso that callers can writeexcept TimeoutError:and still catch this. TheCosmicprefix keeps it from shadowing the built-in if a user doesfrom pycosmicsdk import *.
- class pycosmicsdk.ProtocolError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised on protocol-level errors not attributable to firmware state.
- class pycosmicsdk.CosmicSDKLibraryNotFound(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when the bundled CosmicSDK shared library cannot be located.
- class pycosmicsdk.CosmicSDKVersionError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when the loaded CosmicSDK library reports an incompatible version.
- class pycosmicsdk.CosmicInternalError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorRaised when an internal SDK invariant is violated (an SDK bug, not user error).
- class pycosmicsdk.FirmwareError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
CosmicErrorBase for errors reported by device firmware via a status code.
- class pycosmicsdk.I2cError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised on the I2C bus.
- class pycosmicsdk.I2cDutyCycleRefusedError(status_code: StatusCode | int, operation: str | None = None, message: str = '', frequency_hz: int | None = None, high_pct: int | None = None)
Bases:
FirmwareErrorRaised when firmware refuses
I2cController.set_duty_cycle’s ratio.status_codeisFW_INVALID_PARAMETER. The refusal reply still carries the settings the bus keeps, so they are here rather than lost: a caller learns what the bus runs at without a second command. AFirmwareError, not anI2cError, because the code is a generic one.
- class pycosmicsdk.UartError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised on the UART interface.
- class pycosmicsdk.I3cError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised on the I3C bus.
- class pycosmicsdk.SpiError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised on the SPI interface.
- class pycosmicsdk.GpioError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised on a GPIO line.
- class pycosmicsdk.SystemFirmwareError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorFirmware error raised by system-level commands (voltage, reset, …).
- class pycosmicsdk.DebugError(status_code: StatusCode | int, operation: str | None = None, message: str = '')
Bases:
FirmwareErrorError from a debug probe’s debug session (attach, halt, memory, breakpoints).
- class pycosmicsdk.TargetFlashVerifyError(status_code: StatusCode | int, operation: str | None = None, message: str = '', first_mismatch: int | None = None)
Bases:
DebugErrorRaised when
TargetFlash.write(..., verify=True)reads back different bytes.status_codeisDEBUGPROBE_TFLASH_VERIFY_MISMATCH(0x1D07). The flash was erased and programmed; only the readback disagreed.