SFT-8184: restore the NOT_BIP39_MODE and MULTISIG_STORAGE_IDX_ERROR error codes
What changed, and why it matters
This commit fixes a simple but consequential typo in a list of error names: a missing comma had caused two distinct error codes to be merged into one long, meaningless name. As a result, any code that tried to use either of the two intended error codes would have crashed with an AttributeError. The patch restores the comma and adds a unit test to catch this in the future. It is a reliability/availability bug rather than a direct theft-of-funds vulnerability, but on a security device like Passport it could cause unexpected failures during multisig or seed-related operations.
Merge the fix and ensure the new unit test runs in CI. Review other tuple/list-based enums in the codebase for similar missing-comma issues. Consider whether any user-facing flows could have hit the AttributeError and whether they need recovery guidance.
Security signals we found
Missing comma in a Python tuple silently merged two error-code identifiers
Runtime AttributeError would occur on any path referencing the two intended error codes
Affected codes relate to multisig storage index handling and BIP39 seed mode enforcement
No cryptographic bypass or direct private-key exposure is visible in the diff
Fix is accompanied by a regression unit test
Evidence from the diff
In ports/stm32/boards/Passport/modules/errors.py, the tuple defining Error enum members had ‘MULTISIG_STORAGE_IDX_ERROR’ immediately followed by ‘NOT_BIP39_MODE’ without a comma. Python therefore concatenated the two string literals into a single member named ‘MULTISIG_STORAGE_IDX_ERRORNOT_BIP39_MODE’, leaving the intended names undefined. Code referencing Error.MULTISIG_STORAGE_IDX_ERROR or Error.NOT_BIP39_MODE would raise AttributeError at runtime. The fix adds the missing comma. A new unit test iterates the expected names, asserts they exist and are distinct, and asserts the concatenated artifact does not exist.
Changed components
ports/stm32/boards/Passport/modules/errors.pyFoundation Passport firmware error-code enumAny firmware paths raising or catching MULTISIG_STORAGE_IDX_ERROR or NOT_BIP39_MODEInspect captured patch +24 / −1
### ports/stm32/boards/Passport/modules/errors.py
@@ -15,7 +15,7 @@
'INVALID_BACKUP_FILE_HEADER',
'MICROSD_FORMAT_ERROR',
'MICROSD_CARD_MISSING',
- 'MULTISIG_STORAGE_IDX_ERROR'
+ 'MULTISIG_STORAGE_IDX_ERROR',
'NOT_BIP39_MODE',
'OUT_OF_MEMORY_ERROR',
'PSBT_FATAL_ERROR',
### ports/stm32/boards/Passport/modules/tests/test_unit.py
@@ -16,6 +16,10 @@ def doit(file):
return doit
+def test_error_codes(test):
+ assert test('error_codes.py') == b'OK'
+
+
def test_ext_settings(test):
assert test('ext_settings.py') == b'OK'
### ports/stm32/boards/Passport/modules/tests/unit/error_codes.py
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: © 2026 Foundation Devices, Inc. <hello@foundation.xyz>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# Error codes are built from a tuple of names, so a missing comma silently
+# concatenates two of them into one member. The intended names then don't
+# exist, and every reference to them raises AttributeError at runtime.
+
+from errors import Error
+
+
+for name in ('MULTISIG_STORAGE_IDX_ERROR', 'NOT_BIP39_MODE'):
+ assert hasattr(Error, name), name
+
+assert Error.MULTISIG_STORAGE_IDX_ERROR != Error.NOT_BIP39_MODE
+
+# The exact symptom of the missing comma this test was added for.
+assert not hasattr(Error, 'MULTISIG_STORAGE_IDX_ERRORNOT_BIP39_MODE')
+
+return_value.write(b'OK')Why this scored 47/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.