Calling esp_ota_mark_app_valid_cancel_rollback is a release decision made by the running firmware. Put it behind a small, explicit acceptance test, and preserve evidence of why that test passed. The difficult part is deciding what healthy means before a device becomes unreachable. A successful download is only the beginning of that decision.
What the call actually confirms
With CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE enabled, confirmation changes the running trial image to ESP_OTA_IMG_VALID. The API contract and state transitions are documented in the ESP-IDF OTA guide for ESP32 v5.3. This article uses that version as its reference; check the documentation for the release used by your product.
Confirmation should represent a defined acceptance milestone. For a connected sensor, write down which capabilities the previous release provided and which the candidate must demonstrate. A boot message alone proves little about a device whose main job is collecting readings and delivering them to a backend.
Make the decision visible in logs. Include the firmware identity, running partition, observed image state and health-check result. Keep acceptance separate from a dashboard's download-complete event. Operations staff need to distinguish delivery, trial execution and successful validation when investigating an update that appears to disappear.
The image states behind it
With rollback enabled, selecting an updated OTA partition makes it ESP_OTA_IMG_NEW. The bootloader changes NEW to ESP_OTA_IMG_PENDING_VERIFY before trial execution. Confirmation makes it VALID. Explicit rejection uses ESP_OTA_IMG_INVALID; a subsequent boot encountering an unconfirmed PENDING_VERIFY image changes it to ESP_OTA_IMG_ABORTED and excludes it from normal selection. Recovery needs an eligible working image.
Read the running partition's state with esp_ota_get_state_partition, checking its return value. Keep application diagnostics distinct from the image state: a label in OTA metadata cannot tell you whether a particular sensor initialized correctly. Document the expected state at every point in the test procedure so unexpected transitions become investigation evidence.
The OTA API header documents the query and confirmation interfaces. Avoid interpreting a failed state query as ordinary validated operation. Give that failure its own diagnostic path, with outputs held in the product's defined safe condition.
When to call it
Run a bounded acceptance test after the services needed by the product have initialized. Check that configuration loads and passes schema validation, the required sensor can provide a plausible observation, and critical tasks remain alive. If connectivity is essential, establish the network and perform a meaningful authenticated backend exchange rather than treating an assigned address as proof of service.
Define which failures demonstrate a bad image and which are temporary environmental problems. A remote endpoint outage can affect both old and new firmware. Making that endpoint an unconditional acceptance dependency can trigger rollback during an unrelated service incident. Choose the policy deliberately and test it under controlled loss of connectivity.
In the control-flow sketch below, the application supplies health_check_with_deadline and report_ota_error; headers, initialization and recovery handling belong in the surrounding firmware. The health check must return within the product's chosen startup budget.
const esp_partition_t *p = esp_ota_get_running_partition();
esp_ota_img_states_t state;
esp_err_t err = esp_ota_get_state_partition(p, &state);
if (err != ESP_OK) {
report_ota_error(err);
} else if (state == ESP_OTA_IMG_PENDING_VERIFY) {
bool healthy = health_check_with_deadline();
err = healthy ? esp_ota_mark_app_valid_cancel_rollback()
: esp_ota_mark_app_invalid_rollback_and_reboot();
if (err != ESP_OK) report_ota_error(err);
}
Do not treat report_ota_error as a complete recovery policy. Decide whether the device must inhibit outputs, persist a fault or enter a service mode after an API error. Keep the acceptance task independent of optional background work so telemetry uploads cannot silently postpone the decision indefinitely.
The other path: rolling back on purpose
Use esp_ota_mark_app_invalid_rollback_and_reboot when the candidate fails its acceptance test. Check the error path: a recovery call is not evidence that a suitable fallback exists. Maintain a known working image and test the actual partition layout used in production.
A reset before confirmation also ends the trial on the following boot when rollback is enabled. That includes a crash or power loss during acceptance. Capture reset context where possible, but avoid logging designs that depend on the failed application staying alive long enough to explain itself.
Plan recovery beyond the image bytes. A candidate might migrate persistent configuration before failing. If the previous firmware cannot read that configuration, returning to its partition does not restore service. Use reversible changes, preserve compatible data or define a separate recovery mechanism before expanding a rollout.
Common mistakes
Confirming as the first action in app_main tests almost nothing about the updated product. Place confirmation after the smallest meaningful set of checks, then continue longer observations through staged deployment. Startup acceptance and fleet monitoring answer different questions and should have separate criteria.
Never confirming is another operational defect. Exercise every branch that can leave acceptance waiting: unavailable credentials, missing configuration, delayed sensor startup and a backend that accepts a connection but never answers. Assign ownership of the deadline rather than relying on unrelated task timeouts to produce a predictable result.
Check the built bootloader configuration, not just the intended project settings. A lab device assembled from old and new build artifacts can give misleading results. Record build identifiers and verify the bootloader and application combination used by the deployed cohort.
App rollback is availability recovery; anti-rollback restricts booting older security versions. The anti-rollback documentation explains the eFuse security-version constraint. An older image can be retained yet ineligible. Review security-version policy alongside recovery planning before changing it.
Testing the rollback path
Start with a known working image A, exercise its normal service and retain its identity in the test record. Deliver candidate B through the real OTA path. Add a controlled acceptance failure to B and verify that the device returns to A, reconnects and performs its ordinary job. A version string on a serial terminal is insufficient evidence of restored service.
Repeat with a candidate that resets during its trial, before confirmation. Repeat again with controlled power removal at that stage. Inspect boot logs and the rejected partition's state where available. Use a safe fixture that prevents actuator movement during power cycling.
Finally test the success path: let B pass acceptance, confirm it, reboot and verify continued operation. Repeat the failure tests with the production persistence layout and connectivity conditions. Record expected and observed outcomes. ESP32 OTA updates in production provides the broader rollout context; production readiness requires recovery to be demonstrated as an operating behavior, not merely an API call.