Overview
When an unexpected, severe error occurs in CircuitPython, CircuitPython will enter safe mode. An error that causes safe mode is so serious that it is not possible to proceed; otherwise CircuitPython would raise a Python exception, which you could catch and handle. You can also initiate safe mode yourself manually or in your CircuitPython program.
Safe mode prevents boot.py and code.py from running until you exit safe mode. It also enables running a special file called safemode.py that can exit safe mode automatically.
There are multiple possible causes for safe mode:
- An unexpected software error occurred, possibly due to a bug in CircuitPython or underlying software that it uses.
- A hardware failure occurred.
- The power dipped to too low a voltage.
- There is an error in boot.py or safemode.py.
- Some CircuitPython code deliberately entered safe mode.
- You pressed one or more buttons to enter safe mode manually. You may want to do this if boot.py or code.py are doing something you cannot recover from, such as restarting over and over, or setting CIRCUITPY to be read-only.
When safe mode is triggered, CircuitPython saves the reason for going into safe made, and then forces a hard reset of the board. After the reset, it skips running boot.py and code.py, as mentioned. Instead, it simply prints the reason for safe mode on the console. You can then enter the CircuitPython REPL, edit files on the CIRCUITPY drive to fix the problem to clear safe mode, or just press RESET again, if the problem is transient.
For instance, here is the kind of thing you would see in the console if there was a power problem:
Auto-reload is off. Running in safe mode! Not running saved code. You are in safe mode because: The power dipped. Make sure you are providing enough power. Press reset to exit safe mode. Press any key to enter the REPL. Use CTRL-D to reload. Adafruit CircuitPython 8.1.0 on 2023-03-30; Adafruit Feather RP2040 with rp2040
In the rest of this guide, you'll learn:
- More about the details of the causes of safe mode
- How to enter safe mode deliberately
- How to recover from safe mode, manually or using safemode.py
Page last edited March 08, 2024
Text editor powered by tinymce.
Safe Mode Reasons
The reasons for entering safe mode fall into several categories. All safe mode reasons are enumerated as constants in the supervisor.SafeModeReason class. The reasons are listed below, in code text. The bold text is what is printed in the console. Refer to the documentation for the latest list and for more details.
Internal Software Errors
These safe mode reasons reflect internal errors in CircuitPython, probably because of a bug:
-
Heap allocation when VM not running.
SafeModeReason.GC_ALLOC_OUTSIDE_VM -
Failed to write internal flash.
SafeModeReason.FLASH_WRITE_FAIL
This might be a hardware error as well. -
Hard fault: memory access or instruction error.
SafeModeReason.HARD_FAULT
This is often an out-of-bounds memory reference. It does not indicate a hardware failure. -
Interrupt error.
SafeModeReason.INTERRUPT_ERROR
Some error associated with interrupts. -
NLR jump failed. Likely memory corruption.
SafeModeReason.NLR_JUMP_FAILED
This is a "Non Local Return" (exception handling) problem. -
Unable to allocate the heap.
SafeModeReason.NO_HEAP -
Third-party firmware fatal error.
SafeModeReason.SDK_FATAL_ERROR
Reported when third-party software used by CircuitPython reports an unrecoverable error. -
Internal watchdog timer expired.
SafeModeReason.WATCHDOGAn internal watchdog timer went off. Something is looping or taking more time than expected. This is not the watchdog timer used formicrocontroller.watchdog. Ifmicrocontroller.watchdogis enabled to do a reset, it will do a regular reset; it will not go into safe mode.
Hardware Failures
-
CIRCUITPY drive could not be found or created.
SafeModeReason.NO_CIRCUITPY
This is often due to a hardware failure in the external flash chip on the board.
Power Problem
A power supply issue may cause the BROWNOUT safe mode. Your battery may be getting low, or you may be trying to draw too much current from a USB port or other power source:
-
The power dipped. Make sure you are providing enough power.
SafeModeReason.BROWNOUT
Stack Size
The STACK_OVERFLOW safe mode may be correctable by increasing the stack size, using supervisor.runtime.next_stack_limit:
- Heap was corrupted because the stack was too small. Increase stack size.
-
Stack overflow. Increase stack size [changed in CircuitPython 9.0.0]
SafeModeReason.STACK_OVERFLOW
boot.py USB Problems
These safe modes are due to erroneous USB configurations set up in boot.py:
- USB devices need more endpoints than are available.
SafeModeReason.USB_TOO_MANY_ENDPOINTS - USB devices specify too many interface names.
SafeModeReason.USB_TOO_MANY_INTERFACE_NAMES -
Boot device must be first (interface #0).
SafeModeReason.USB_BOOT_DEVICE_NOT_INTERFACE_ZERO
safemode.py Problem
If an error is encountered while running safemode.py, you'll get this:
- Error in safemode.py.
SafeModeReason.SAFE_MODE_PY_ERROR
Programmatic Safe Mode
-
The `microcontroller` module was used to boot into safe mode.
SafeModeReason.PROGRAMMATIC
You can write code to enter safe mode using microcontroller.on_next_reset():
import microcontroller microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE) microcontroller.reset()
Manual Safe Mode
You can press some combination of buttons that entered safe mode. The button(s) you use vary per board. On many boards, the RGB or single-color status LED will blink briefly a few times after you press RESET. If you press RESET during the blinks, the board will enter safe mode. On other boards, you will enter safe mode if you hold down one or more buttons and press RESET. For instance on Circuit Playground boards, you enter safe mode by holding down both button A and button B while pressing RESET.
- You pressed the BOOT button at start up.
- You pressed the reset button during boot.
- You pressed button A at start up.
- [and similar messages]
SafemodeReason.USER
Manual (aka USER) safe mode is useful if your program causes a boot loop and you can't control-C the program. Or, you may have set CIRCUITPY to be read-only from the host computer by doing a storage.remount() in boot.py. If you didn't provide a way out of this (by, say, not doing the remount when a button is pressed), then entering safe mode manually will skip boot.py and you'll be able to edit your CIRCUITPY files.
Page last edited March 08, 2024
Text editor powered by tinymce.
safemode.py
safemode.py Runs When Safe Mode Occurs
Normally, safe mode indicates some serious problem, and you might want to investigate that problem. However, if you are running a program that is unattended, you may want start it up again, despite getting a serious safe mode error. The safemode.py file lets you do that.
When safe mode occurs, boot.py and code.py are not run. However, if and only if safe mode occurs, and a file named safemode.py exists, that file is executed.
Like boot.py, safemode.py is run before any connection is made via USB, or via the WiFi or BLE workflow. Anything printed by safemode.py is not written anywhere (unlike boot.py, which writes to boot_out.txt).
Doing a Reset in safemode.py
The main use of safemode.py is to decide automatically when to leave safe mode and restart normally, with a hard reset. For instance, if you're running a long-lived program that uses WiFi, fatal errors can sometimes occur due to various bugs. So if you want to just ignore safe mode and try again, this simple safemode.py will leave safe mode and do a reset, as if you pressed the RESET button.
import microcontroller microcontroller.reset()
Conditional Resetting in safemode.py
In other cases, you may want to restart only on certain safe mode problems. For instance, you may want to protect only against power outages, and restart automatically if they occur. In that case you can check the reason the safe mode happened in safemode.py:
import microcontroller
import supervisor
if supervisor.runtime.safe_mode_reason == supervisor.SafeModeReason.BROWNOUT:
microcontroller.reset() # Reset and start over.
# Otherwise, do nothing. The safe mode reason will be printed in the
# console, and nothing will run.
Sleeping in safemode.py
Another reason for a power brownout is that you may, for instance, have a solar-powered battery charger. When the battery is low, it may be just enough to start the microcontroller, but not enough to continue running. So you might want to sleep after a brownout, and try again later. You could do that in code.py after checking the battery voltage. Or you could sleep for a few minutes in safemode.py and then reset to try again:
# safemode.py
import alarm
import microcontroller
import supervisor
import time
if supervisor.runtime.safe_mode_reason == supervisor.SafeModeReason.BROWNOUT:
# Sleep for ten minutes and then run code.py again.
time_alarm = alarm.time.TimeAlarm(monotonic_time = time.monotonic() + 10*60)
alarm.exit_and_deep_sleep_until_alarms(time_alarm)
Manual (USER) Safe Mode Skips safemode.py
When you force safe mode by pressing one or more buttons, safemode.py is not run. The reasoning is that you are present, and you have decided not to run anything at all. For instance, you may need to fix an error in safemode.py itself. So when supervisor.runtime.safe_mode_reason == USER, safemode.py is skipped.
Page last edited March 08, 2024
Text editor powered by tinymce.