diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9714f8bd609..d9bb2f5fd44 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1705,8 +1705,10 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes // Null-terminate state in-place for safe c_str() usage (e.g., parse_number in callbacks). // Safe: decode is complete, byte after string data was already consumed during parse, // and frame helpers reserve RX_BUF_NULL_TERMINATOR extra byte in rx_buf_. + // const_cast is safe: msg references rx_buf_ data which is mutable; the const& handler + // signature is a generated protobuf pattern, not a true immutability contract. if (!msg.state.empty()) { - msg.state.null_terminate_in_place(); + const_cast(msg.state).null_terminate_in_place(); } for (auto &it : this->parent_->get_state_subs()) { @@ -1729,7 +1731,9 @@ void APIConnection::on_execute_service_request(const ExecuteServiceRequest &msg) // Null-terminate string args in-place for safe c_str() usage in YAML service triggers. // Safe: full ExecuteServiceRequest decode is complete, all bytes in rx_buf_ consumed, // and frame helpers reserve RX_BUF_NULL_TERMINATOR extra byte for the last field. - for (auto &arg : msg.args) { + // const_cast is safe: msg references rx_buf_ data which is mutable; the const& handler + // signature is a generated protobuf pattern, not a true immutability contract. + for (auto &arg : const_cast(msg).args) { if (!arg.string_.empty()) { arg.string_.null_terminate_in_place(); } diff --git a/esphome/core/string_ref.h b/esphome/core/string_ref.h index 3a66f3d9d59..d25b59632cd 100644 --- a/esphome/core/string_ref.h +++ b/esphome/core/string_ref.h @@ -84,9 +84,7 @@ class StringRef { /// Write a null terminator at base_[len_] in-place. /// Caller must guarantee that the byte at base_[len_] is writable memory /// (e.g., the RX_BUF_NULL_TERMINATOR byte reserved by frame helpers after decode). - /// Marked const because StringRef itself is not modified; the underlying buffer - /// (owned by frame helper rx_buf_) is mutated via const_cast. - void null_terminate_in_place() const { const_cast(base_)[len_] = '\0'; } + void null_terminate_in_place() { const_cast(base_)[len_] = '\0'; } /// Find first occurrence of substring, returns std::string::npos if not found. /// Note: Requires the underlying string to be null-terminated.