call_loop(), mark_failed(), and call_dump_config() Are No Longer Virtual
Component::call_loop(), Component::mark_failed(), and Component::call_dump_config() are no longer virtual methods. External components that override any of these methods must remove them entirely and move logic to loop() or dump_config(). This saves 800+ bytes of flash globally from vtable elimination.
This is a breaking change for external components in ESPHome 2026.3.0 and later.
Background
PR #14083: Devirtualize call_loop() and mark_failed() in Component PR #14355: Devirtualize call_dump_config()
These methods were virtual since the original 2019 codebase, designed to let intermediate base classes inject behavior between the framework and the user's loop() / dump_config() without requiring base class calls. The components that historically used these overrides have all been refactored:
MQTTComponent::call_loop()— removed in #13356 (Jan 2026) when MQTT was restructuredESP32BLE::mark_failed()— removed in #4173 (Jan 2023) when BLE was refactored to use event handlersMQTTComponent::call_dump_config()— removed in #14355; the override incorrectly skippeddump_config()for internal entities
After those architectural improvements, zero overrides remained. Making these methods non-virtual eliminates one vtable slot per method from every Component-derived class (~100+ vtables in a typical build), saving 800+ bytes of flash.
What's Changing
// Before (virtual)
class Component {
public:
virtual void call_loop();
virtual void mark_failed();
virtual void call_dump_config();
};
// After (non-virtual)
class Component {
public:
void call_loop();
void mark_failed();
void call_dump_config();
};
Any class that declares call_loop() override, mark_failed() override, or call_dump_config() override will fail to compile.
Who This Affects
External components that override call_loop(), mark_failed(), or call_dump_config(). No known external components currently override any of these methods — they were virtual with zero overrides in the wild.
Standard YAML configurations are not affected.
Migration Guide
Remove overrides and move logic to loop()/dump_config()
Simply removing the override keyword is not enough — a same-named non-virtual method in a derived class will never be called by the framework (calls through Component* always dispatch to Component::call_loop()). You must remove the method entirely and move logic to the appropriate virtual hook.
// Before — fails to compile
class MyComponent : public Component {
public:
void call_loop() override {
// Custom per-loop logic
}
void call_dump_config() override {
// Custom config output
}
void mark_failed() override {
// Custom failure handling
}
};
// After — use loop(), dump_config(), and is_failed() instead
class MyComponent : public Component {
public:
void loop() override {
// Former call_loop() logic goes here
if (this->is_failed()) {
// Former mark_failed() follow-up handling goes here
return;
}
}
void dump_config() override {
// Former call_dump_config() logic goes here
}
};
Supporting Multiple ESPHome Versions
Since override on a non-virtual method is a compile error, you need a version guard:
#if ESPHOME_VERSION_CODE >= VERSION_CODE(2026, 3, 0)
// These methods are no longer virtual — do not override
#else
void call_loop() override { /* ... */ }
void mark_failed() override { /* ... */ }
void call_dump_config() override { /* ... */ }
#endif
Timeline
- ESPHome 2026.3.0 (March 2026):
call_loop(),mark_failed(), andcall_dump_config()are no longer virtual —overridekeyword causes compile error
Finding Code That Needs Updates
# Find call_loop() overrides
grep -rn 'call_loop().*override' your_component/
# Find mark_failed() overrides
grep -rn 'mark_failed().*override' your_component/
# Find call_dump_config() overrides
grep -rn 'call_dump_config().*override' your_component/
Questions?
If you have questions about migrating your external component, please ask in:
- ESPHome Discord - #devs channel
- ESPHome GitHub Discussions
Related Documentation
- PR #14083: Devirtualize call_loop() and mark_failed() in Component
- PR #14355: Devirtualize call_dump_config()
Comments
Feel free to leave a comment here to discuss this post wth 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.