# CircuitPython Safe Mode

## 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 c **ode.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&nbsp; **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.&nbsp;Instead, it simply prints the reason for safe mode on the console. You can then enter the [CircuitPython REPL](https://learn.adafruit.com/welcome-to-circuitpython/the-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:

```terminal
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**

# CircuitPython Safe Mode

## 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&nbsp;`code text`.&nbsp;&nbsp;The **bold text** is what is printed in the console. Refer to the [documentation](https://docs.circuitpython.org/en/latest/shared-bindings/supervisor/index.html#supervisor.SafeModeReason) 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.WATCHDOG`An internal watchdog timer went off. Something is looping or taking more time than expected. This is **_not_** the watchdog timer used for `microcontroller.watchdog`. If `microcontroller.watchdog` is 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](https://learn.adafruit.com/customizing-usb-devices-in-circuitpython/how-many-usb-devices-can-i-have#using-too-many-devices-3096628) than are available.**  
`SafeModeReason.USB_TOO_MANY_ENDPOINTS`
- **USB devices specify too many interface names.&nbsp;**  
`SafeModeReason.USB_TOO_MANY_INTERFACE_NAMES`
- **Boot device [must be first](https://docs.circuitpython.org/en/latest/shared-bindings/usb_hid/index.html#usb_hid.enable) (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()](https://docs.circuitpython.org/en/latest/shared-bindings/microcontroller/index.html#microcontroller.on_next_reset):

```python
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](https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting#circuitpython-7-dot-0-0-and-later-2978455) 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.

# CircuitPython Safe Mode

## 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.&nbsp;

```python
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** :

```python
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:

```python
# 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.


## Related Guides

- [Matrix Keypad](https://learn.adafruit.com/matrix-keypad.md)
- [Reading QR Codes with the Tiny Code Reader](https://learn.adafruit.com/reading-qr-codes-with-the-tiny-code-reader.md)
- [Snow Globe with Circuit Playground Bluefruit](https://learn.adafruit.com/snow-globe-bluefruit-cpb.md)
- [Introducing Adafruit CLUE](https://learn.adafruit.com/adafruit-clue.md)
- [Pyloton: CircuitPython Cycling Computer](https://learn.adafruit.com/pyloton.md)
- [CircuitPython Essentials](https://learn.adafruit.com/circuitpython-essentials.md)
- [CircuitPython 101: State Machines, Two Ways](https://learn.adafruit.com/circuitpython-101-state-machines.md)
- [Adafruit MAX17048 LiPoly / LiIon Fuel Gauge and Battery Monitor](https://learn.adafruit.com/adafruit-max17048-lipoly-liion-fuel-gauge-and-battery-monitor.md)
- [Adafruit Audio BFF](https://learn.adafruit.com/adafruit-audio-bff.md)
- [PyPortal Roku Remote](https://learn.adafruit.com/pyportal-roku-remote.md)
- [Adafruit 1.14" 240x135 Color Newxie TFT Display](https://learn.adafruit.com/adafruit-1-14-240x135-color-newxie-tft-display.md)
- [MacroPad Summer Olympics Hotkeys](https://learn.adafruit.com/macropad-olympic-hotkeys.md)
- [LED Bullwhip with Motion & Sound Reactivity](https://learn.adafruit.com/led-bullwhip.md)
- [Knobby Sequencer](https://learn.adafruit.com/knobby-sequencer.md)
- [May Pad Macropad with the KB2040, KMK, and CircuitPython](https://learn.adafruit.com/maypad-macropad-with-the-kb2040-kmk-and-circuitpython.md)
