TDLib may crash with SIGABRT while leaving a group call.
The crash happens inside:
td::GroupCallManager::get_recent_speakers()
during the following flow:
leaveGroupCall
-> send_update_group_call
-> get_recent_speakers
-> process_check_error
-> abort()
This causes the entire process using TDLib to terminate.
⸻
Crash stack trace (simplified)
td::process_fatal_error()
td::LogInterface::append()
td::Logger::~Logger()
td::detail::process_check_error()
td::GroupCallManager::get_recent_speakers()
td::GroupCallManager::send_update_group_call()
td::GroupCallManager::leave_group_call()
td::Requests::on_request()
td::Td::run_request()
⸻
Environment
- TDLib version: 1.8.65 / 1.8.66
- Platform: Linux x86_64
- Usage: long-running application with multiple TDLib clients and group calls
⸻
Observed behavior
The crash was reproduced multiple times with the same stack trace.
The failure occurs while processing leaveGroupCall.
The application itself does not intentionally call internal TDLib functions involved in the crash path; the flow can also be triggered by TDLib internal state updates.
⸻
Suspected location
td/telegram/GroupCallManager.cpp
Function:
vector<td_api::object_ptr<td_api::groupCallRecentSpeaker>> GroupCallManager::get_recent_speakers(
const GroupCall *group_call, bool for_update)
The function contains assumptions enforced by CHECK:
CHECK(group_call != nullptr && group_call->is_inited);
and:
CHECK(recent_speakers != nullptr);
A failure of either condition results in SIGABRT.
⸻
Temporary workaround
Replacing the fatal checks with defensive returns prevents the whole process from terminating:
if (group_call == nullptr || !group_call->is_inited) {
return Auto();
}
if (recent_speakers == nullptr) {
return Auto();
}
After applying this change, the application continues running instead of aborting.
⸻
Question
Could this indicate an internal TDLib state synchronization issue around:
- leave_group_call()
- on_call_state_updated()
- group_call_recent_speakers_
Should these cases be handled gracefully instead of using fatal CHECK assertions?
Any guidance on the expected invariant or a proper fix would be appreciated.
TDLib may crash with SIGABRT while leaving a group call.
The crash happens inside:
td::GroupCallManager::get_recent_speakers()
during the following flow:
leaveGroupCall
-> send_update_group_call
-> get_recent_speakers
-> process_check_error
-> abort()
This causes the entire process using TDLib to terminate.
⸻
Crash stack trace (simplified)
td::process_fatal_error()
td::LogInterface::append()
td::Logger::~Logger()
td::detail::process_check_error()
td::GroupCallManager::get_recent_speakers()
td::GroupCallManager::send_update_group_call()
td::GroupCallManager::leave_group_call()
td::Requests::on_request()
td::Td::run_request()
⸻
Environment
⸻
Observed behavior
The crash was reproduced multiple times with the same stack trace.
The failure occurs while processing leaveGroupCall.
The application itself does not intentionally call internal TDLib functions involved in the crash path; the flow can also be triggered by TDLib internal state updates.
⸻
Suspected location
td/telegram/GroupCallManager.cpp
Function:
vector<td_api::object_ptr<td_api::groupCallRecentSpeaker>> GroupCallManager::get_recent_speakers(
const GroupCall *group_call, bool for_update)
The function contains assumptions enforced by CHECK:
CHECK(group_call != nullptr && group_call->is_inited);
and:
CHECK(recent_speakers != nullptr);
A failure of either condition results in SIGABRT.
⸻
Temporary workaround
Replacing the fatal checks with defensive returns prevents the whole process from terminating:
if (group_call == nullptr || !group_call->is_inited) {
return Auto();
}
if (recent_speakers == nullptr) {
return Auto();
}
After applying this change, the application continues running instead of aborting.
⸻
Question
Could this indicate an internal TDLib state synchronization issue around:
Should these cases be handled gracefully instead of using fatal CHECK assertions?
Any guidance on the expected invariant or a proper fix would be appreciated.