Summary
Flasher.attachFlash (pkg/espflasher/flasher.go#L907-L913) calls spiSetParams(4*1024*1024, ...) with the flash size hardcoded to 4 MB, regardless of what FlasherOptions.FlashSize is set to or what detectFlashSize() finds on the chip. The stub uses this value as a write bound — any write past 4 MB is rejected with status 0xC4 (RESPONSE_FAILED_SPI_OP), even on chips with 8 MB or larger flash.
Reproduction
ESP32-S3 with 8 MB flash. Use FlashImages to write two parts:
- App image at
0x010000 (~1.6 MB) — succeeds
- LittleFS / storage image at
0x6F0000 (within the 8 MB chip but past the hardcoded 4 MB bound) — fails immediately on the first compressed block:
Writing 839680 bytes at 0x006F0000...
Compressed 839680 bytes to 253336 (30%)
Flash begin: 253336 bytes at 0x006F0000 (16 compressed blocks)
flash at 0x006F0000: flash block 1 of 16: command 0x11 failed: status=0xC4 error=0x00 (unknown error)
The same images flash successfully via esptool.py and esptool-js, both of which pass the actual chip size to SPI_SET_PARAMS. esptool's cmds.py#L1852 calls esp.flash_set_parameters(flash_size_bytes(flash_size)) with a comment noting "the flash chip should be configured with the real size".
Why this happens
The 4 MB written into config.flash_size (esp-flasher-stub command_handler.c#L592) is consulted by the stub's flash-write path; an over-bound write returns RESPONSE_FAILED_SPI_OP = 0xC400, surfaced to the host as status=0xC4. The library's detectFlashSize() runs after attachFlash() in FlashImages and only updates the value used to patch image headers — it never re-pushes the corrected size to the stub.
Suggested fix
- Add a helper to convert the existing
1MB/2MB/4MB/.../128MB size strings to bytes.
- Make
attachFlash derive the bytes from f.opts.FlashSize (falling back to 4 MB only if unset/keep/unrecognised).
- After
detectFlashSize() runs in FlashImages, re-push the SPI params so the stub gets the real chip size before any writes.
I have a working patch verified on ESP32-S3 / 8 MB hardware that previously failed at 0x6F0000 and now flashes through to MD5 verify. Happy to send a PR.
Summary
Flasher.attachFlash(pkg/espflasher/flasher.go#L907-L913) callsspiSetParams(4*1024*1024, ...)with the flash size hardcoded to 4 MB, regardless of whatFlasherOptions.FlashSizeis set to or whatdetectFlashSize()finds on the chip. The stub uses this value as a write bound — any write past 4 MB is rejected with status0xC4(RESPONSE_FAILED_SPI_OP), even on chips with 8 MB or larger flash.Reproduction
ESP32-S3 with 8 MB flash. Use
FlashImagesto write two parts:0x010000(~1.6 MB) — succeeds0x6F0000(within the 8 MB chip but past the hardcoded 4 MB bound) — fails immediately on the first compressed block:The same images flash successfully via
esptool.pyandesptool-js, both of which pass the actual chip size toSPI_SET_PARAMS. esptool's cmds.py#L1852 callsesp.flash_set_parameters(flash_size_bytes(flash_size))with a comment noting "the flash chip should be configured with the real size".Why this happens
The 4 MB written into
config.flash_size(esp-flasher-stub command_handler.c#L592) is consulted by the stub's flash-write path; an over-bound write returnsRESPONSE_FAILED_SPI_OP = 0xC400, surfaced to the host asstatus=0xC4. The library'sdetectFlashSize()runs afterattachFlash()inFlashImagesand only updates the value used to patch image headers — it never re-pushes the corrected size to the stub.Suggested fix
1MB/2MB/4MB/.../128MBsize strings to bytes.attachFlashderive the bytes fromf.opts.FlashSize(falling back to 4 MB only if unset/keep/unrecognised).detectFlashSize()runs inFlashImages, re-push the SPI params so the stub gets the real chip size before any writes.I have a working patch verified on ESP32-S3 / 8 MB hardware that previously failed at
0x6F0000and now flashes through to MD5 verify. Happy to send a PR.