51fbd0657c400e09ca9ee625ed156f1029937f1d
Found on hardware: the Tools Server field was pointed at Immich's own port instead of the frame server's, so /frame/image was actually hitting Immich and getting back a small error response (~10KB) instead of a 192,000-byte frame. epd_display_stream() logged a size-mismatch warning but called epd_turn_on_display() anyway, physically refreshing the panel with a buffer that was ~95% whatever was left over from before -- visible as "garbage" on screen, overwriting a previously-good image. epd_display_stream() now returns ESP_ERR_INVALID_SIZE instead of refreshing when the stream doesn't supply exactly EPD_FRAME_BYTES. Since this check happens before epd_turn_on_display() is ever called, the pixel data that *did* arrive only ever reached the panel's internal RAM over SPI, not the physically visible display, so aborting here leaves the screen exactly as it was. This also means fetch_and_display() failing now always implies the panel was never touched -- simplified frame_client_run() accordingly (dropped the now-always-true/false out-param that used to distinguish "failed before vs. during streaming", and always shows the FAILED status screen on any fetch/display error, since it's now guaranteed safe to do so).