set_retry Deprecated: Use set_timeout or set_interval Instead
The set_retry, cancel_retry, and RetryResult APIs are deprecated. They will be removed in ESPHome 2026.8.0. Use set_timeout or set_interval instead.
This is a breaking change for external components in ESPHome 2026.2.0 and later.
Background
Section titled “Background”PR #13845: Deprecate set_retry, cancel_retry, and RetryResult
The set_retry API has several problems that make it unsuitable for ESPHome’s embedded environment:
-
Hidden heap allocation. Every
set_retrycall does astd::make_shared<RetryArgs>(), allocating on the heap. This is invisible to component authors and contributes to fragmentation on devices that run for months. -
Wasted flash and RAM. The retry machinery —
RetryArgsstruct,retry_handler(),set_retry_common_(), multiple overloads, and the heavierstd::function<RetryResult(uint8_t)>template instantiation — is compiled into every firmware whether or not any component uses it. -
Confusing immediate-first-execution semantics.
set_retryexecutes the callback immediately (delay=0) before waitinginitial_wait_timefor subsequent attempts. Callers expected it to wait first, likeset_intervaldoes. -
The abstraction didn’t match real usage. All internal callers were using
set_retryas a fixed-interval poll (backoff factor 1.0, always returningRETRY). These are trivially served byset_interval+ counter or chainedset_timeout.
All internal usage has been removed in companion PRs: #13841 (lps22), #13842 (ms8607), #13843 (speaker), #13844 (esp32_hosted).
What’s Changing
Section titled “What’s Changing”The entire set_retry / cancel_retry / RetryResult API is marked ESPDEPRECATED. Using any of these will produce compiler warnings now. The API will be removed in 2026.8.0.
// All of these are deprecated:this->set_retry("name", 100, 5, [](uint8_t) { return RetryResult::RETRY; });this->set_retry(100, 5, [](uint8_t) { return RetryResult::RETRY; });this->cancel_retry("name");RetryResult result = RetryResult::DONE;Who This Affects
Section titled “Who This Affects”External components that call set_retry or cancel_retry, or use RetryResult.
Standard YAML configurations are not affected.
Migration Guide
Section titled “Migration Guide”Pattern 1: Fixed-interval polling (most common)
Section titled “Pattern 1: Fixed-interval polling (most common)”The most common set_retry usage is polling at a fixed interval until a condition is met. Replace with set_interval and a counter:
// Beforevoid MyComponent::update() { this->trigger_measurement(); this->set_retry("read", 5, 10, [this](uint8_t remaining) { if (this->data_ready()) { this->read_data(); return RetryResult::DONE; } return RetryResult::RETRY; });}
// Afterstatic constexpr uint32_t INTERVAL_READ = 0; // numeric ID
void MyComponent::update() { this->trigger_measurement(); this->read_attempts_remaining_ = 10; this->set_interval(INTERVAL_READ, 5, [this]() { if (this->data_ready()) { this->cancel_interval(INTERVAL_READ); this->read_data(); } else if (--this->read_attempts_remaining_ == 0) { this->cancel_interval(INTERVAL_READ); } });}Note: set_interval waits before the first execution, unlike set_retry which fired immediately. This is usually the desired behavior — it gives the hardware time to complete the operation before the first read attempt.
Pattern 2: Retry with backoff
Section titled “Pattern 2: Retry with backoff”For retries with increasing delays, use chained set_timeout:
// Beforethis->set_retry("reset", 5, 3, [this](uint8_t remaining) { if (this->try_reset()) { return RetryResult::DONE; } return RetryResult::RETRY;}, 5.0f); // backoff factor
// Aftervoid MyComponent::setup() { this->reset_attempts_remaining_ = 3; this->reset_interval_ = 5; this->try_reset_();}
void MyComponent::try_reset_() { if (this->try_reset()) { // Success return; } if (--this->reset_attempts_remaining_ > 0) { uint32_t delay = this->reset_interval_; this->reset_interval_ *= 5; // backoff factor this->set_timeout("reset", delay, [this]() { this->try_reset_(); }); } else { this->mark_failed(); }}Pattern 3: Wait for state transition
Section titled “Pattern 3: Wait for state transition”For waiting until something reaches a target state:
// Beforethis->set_retry("wait_stop", 50, 3, [this](uint8_t remaining) { if (this->state_ == STATE_STOPPED) { this->on_stopped(); return RetryResult::DONE; } return RetryResult::RETRY;});
// Afterthis->wait_attempts_remaining_ = 3;this->set_interval("wait_stop", 50, [this]() { if (this->state_ == STATE_STOPPED) { this->cancel_interval("wait_stop"); this->on_stopped(); } else if (--this->wait_attempts_remaining_ == 0) { this->cancel_interval("wait_stop"); }});Supporting Multiple ESPHome Versions
Section titled “Supporting Multiple ESPHome Versions”No version guard is needed. set_interval and set_timeout are available in all ESPHome versions, so migrated code works everywhere.
Timeline
Section titled “Timeline”- ESPHome 2026.2.0 (February 2026): Deprecation warnings active
- ESPHome 2026.8.0 (August 2026):
set_retry,cancel_retry, andRetryResultremoved
Finding Code That Needs Updates
Section titled “Finding Code That Needs Updates”# Find set_retry usagegrep -rn 'set_retry' your_component/grep -rn 'cancel_retry' your_component/grep -rn 'RetryResult' your_component/Questions?
Section titled “Questions?”If you have questions about migrating your external component, please ask in:
- ESPHome Discord - #devs channel
- ESPHome GitHub Discussions
Related Documentation
Section titled “Related Documentation”Copyright © 2026 ESPHome - A project from the Open Home Foundation
Comments
Feel free to leave a comment here to discuss this post with others. You can ask questions, share your experience, or suggest improvements. If you have a question about a specific feature or issue, please consider using the ESPHome Discord. Stick to English and follow ESPHome's code of conduct. These comments exist on a discussion on GitHub, so you can also comment there directly if you prefer.