# It compiled. It flashed. The board still won't boot.

> An ESP32-S3 that flashes fine then loops on 'Invalid image block' is a board-settings problem, not a code problem. How to read the boot log.

[HTML version](https://soldr.ai/docs/esp32-s3-flashed-but-wont-boot)

[Docs](https://soldr.ai/docs) / When it doesn't work

An ESP32-S3 that accepts a sketch and then loops on `Invalid image block, can't boot` is almost never a problem with your code. It is the board settings the image was built with.

4 min read
When it doesn't work
ESP32-S3

## What you're seeing

The upload succeeds. The serial monitor then prints the same block over and over, a few times a second:

```
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x7 (TG0WDT_SYS_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40043ac8
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2820,len:0x10cc
load:0x3cc0c8ff,len:0x3cc11c40
Invalid image block, can't boot.
ets_main.c 329
```

Nothing in that block is produced by your sketch. Every line comes from the mask ROM bootloader, which runs before a single instruction of your code. That is the first and most useful thing it tells you.

## The second load: line is the whole answer

Read the two load lines against each other:

| Line | Address | Length | Verdict |
|---|---|---|---|
| first segment | 0x3fce2820 | 0x10cc = 4,300 bytes | sane — valid internal SRAM, a believable size |
| second segment | 0x3cc0c8ff | 0x3cc11c40 = 1,019,288,640 bytes | impossible |

The second length is roughly **972 MB**. The chip has 512 KB of internal SRAM and at most 32 MB of flash. The address is also **unaligned** — it ends in `ff`, and segment addresses are four-byte aligned.

Look closer and the shape gives the mechanism away: `0x3cc0c8ff` and `0x3cc11c40` are both values in the `0x3Cxx_xxxx` range. The loader is reading an address where a length should be. It is not parsing a slightly wrong image — it has lost its place in the header entirely, so everything after that point is noise.

`rst:0x7 (TG0WDT_SYS_RST)` on the first line is the watchdog resetting a boot that never completed. That is the loop, not the cause. And `boot:0x8 (SPI_FAST_FLASH_BOOT)` confirms the board is trying a normal flash boot, so it is not stuck in download mode and the strapping pins are fine.

## Why "it flashed successfully" proves nothing here

> **A successful flash verifies the write, not the boot.** — The uploader streams bytes to the chip and checks that what came back matches what it sent. That is a test of the transfer . Booting is a different operation: the ROM re-reads those bytes later, using the flash mode, speed and size recorded in the image header. Bytes that verify on write can still decode as garbage on read if that configuration does not match the actual hardware.

Which is why the two facts in the same sentence — "it flashed perfectly" and "invalid image block" — are not a contradiction. They are measurements of two different things.

## Fixing it

The settings that matter are the ones baked into the image header, in this order:

- **Flash size.** If the build says 4 MB and the module has 16 MB, the partition table lands where the ROM is not looking. This alone produces exactly the log above.

- **PSRAM type.** An `R8` module has octal PSRAM and needs the OPI setting. Built as QSPI or disabled, it will not come up.

- **Flash mode.** QIO / DIO / OPI must match the part. A mode mismatch is the classic cause of reads that come back shifted.

- **Flash frequency.** Some modules — clones especially — are not reliable at 80 MHz. 40 MHz is the safe test.

Then **erase the whole chip and reflash**. If only the sketch was rewritten, a bootloader or partition table left over from a differently-configured build will disagree with the new image, and the symptom survives every subsequent upload. In the Arduino IDE that is Erase All Flash Before Sketch Upload; with esptool it is `erase_flash`.

## The variant trap: N16R8 is not "an ESP32-S3"

The suffix on the module is not decoration. On an **ESP32-S3-DevKitC-1 N16R8**, `N16` is 16 MB of flash and `R8` is **8 MB of octal PSRAM**. An S3 SuperMini is a different part with different memory. Selecting a generic "ESP32S3 Dev Module" and leaving the defaults gives you 4 MB flash and no PSRAM — wrong on both counts for an N16R8.

> **Check the marking on the metal can before anything else.** — N16R8 , N8R2 , N8 and the rest each need different settings, and the boot log cannot tell you which board you have. One look at the module answers it.

## Letting Soldr do it

Flashing straight from the browser sidesteps the whole category, because the target is chosen from the board in your build rather than from a menu you set by hand. If you have already taken the code into your own IDE, paste the boot log into Soldr and it will read the segment lines with you.

> **Diagnosis is free** — Pasting a boot log and asking what it means never costs credits.

## If it still won't boot

Work down, stopping when the second `load:` line becomes believable:

- Change **only** the flash size to match the module, erase, reflash. If the log changes, that was it.

- Then PSRAM: OPI for an `R8`, disabled for a module with none.

- Then flash mode, then drop the frequency to 40 MHz.

- If none of that moves the second `load:` line, flash a known-good prebuilt binary for the same board. If that also fails, the module itself is suspect — counterfeit flash is common at the bottom of the market.

The test throughout is that one line. A board that is going to boot prints a second segment with a plausible address and a length in the tens or hundreds of kilobytes.

[Next Reading serial output](https://soldr.ai/docs/reading-serial-monitor-output)
