You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PJSip无法恢复已保持通话:报错PJ_EINVALIDOP(70013)求助

Troubleshooting PJ_EINVALIDOP (70013) When Unholding a Call with PJSIP

Let's break down why you're running into this PJ_EINVALIDOP error when trying to resume a held call, and walk through fixes to get it working.

First, here's the error you're seeing:

Unable to create re-INVITE: Invalid operation (PJ_EINVALIDOP) [status=70013] Error hanging up call: Invalid operation (PJ_EINVALIDOP) [status=70013]

And your current implementation for unholding:

pj_status_t pj_wrapper_unhold_call(pjsua_call_id call_id, char deviceId, char groupId, char *token) { 
    pj_status_t status; 
    if (!pj_wrapper_initialized) { 
        return PJ_EGONE; 
    } 
    status = pjsua_call_reinvite(call_id, PJSUA_CALL_UNHOLD, NULL); 
    if (status != PJ_SUCCESS) { 
        pj_wrapper_show_error("Error hanging up call", status); 
    } 
    return status; 
}

Why This Error Happens

PJ_EINVALIDOP (status code 70013) basically means you're trying to perform an operation that doesn't make sense for the call's current state. For unholding, the most common triggers are:

  • The call isn't actually held: You're trying to unhold a call that's either active, disconnected, or was never held properly in the first place.
  • Call is in a transitional state: If the call is still connecting, disconnecting, or hasn't fully established media yet, a re-INVITE for unhold will fail.
  • PJSIP doesn't recognize the held state: If you used a custom method to hold the call (instead of PJSIP's official PJSUA_CALL_HOLD flag), PJSIP's internal state tracking won't know the call is held, so unhold is seen as invalid.
  • Media session isn't ready: If the call's media hasn't been set up yet (e.g., early media isn't active), you can't send a re-INVITE to unhold.

How to Fix It

Let's update your code to add validation and handle edge cases properly:

1. Add Call State & Media Validation

First, we'll check the call's current state and media status before attempting to unhold. This ensures we only run the unhold operation when it's valid:

pj_status_t pj_wrapper_unhold_call(pjsua_call_id call_id, char deviceId, char groupId, char *token) { 
    pj_status_t status; 
    pjsua_call_info call_info;

    if (!pj_wrapper_initialized) { 
        return PJ_EGONE; 
    }

    // Fetch the latest call state info
    status = pjsua_call_get_info(call_id, &call_info);
    if (status != PJ_SUCCESS) {
        pj_wrapper_show_error("Failed to retrieve call info", status);
        return status;
    }

    // Make sure the call is in an active, confirmed state
    if (call_info.state != PJSUA_CALL_STATE_CONFIRMED) {
        pj_wrapper_show_error("Cannot unhold: call is not active", PJ_EINVALIDOP);
        return PJ_EINVALIDOP;
    }

    // Verify the call is actually held
    if (call_info.media_status != PJSUA_CALL_MEDIA_HOLD) {
        pj_wrapper_show_error("Cannot unhold: call is not in held state", PJ_EINVALIDOP);
        return PJ_EINVALIDOP;
    }

    // Now safely attempt to unhold
    status = pjsua_call_reinvite(call_id, PJSUA_CALL_UNHOLD, NULL); 
    if (status != PJ_SUCCESS) { 
        pj_wrapper_show_error("Error unholding call", status); // Fixed the error message here!
    } 
    return status; 
}

Note: I fixed the error message from "Error hanging up call" to "Error unholding call" since that's what this function is actually doing—this will save you confusion when debugging later.

2. Ensure Hold Was Initiated Correctly

Double-check that when you held the call, you used PJSIP's official API:

// Example of correct hold implementation
pj_status_t hold_status = pjsua_call_reinvite(call_id, PJSUA_CALL_HOLD, NULL);

If you used a custom SDP modification to hold the call, PJSIP won't track that state internally, so it'll reject the unhold request. Stick to the official PJSUA_CALL_HOLD flag for consistency.

3. Handle Transitional States Gracefully

If your app triggers unhold right after holding the call, add a small delay or listen for PJSIP's on_call_state callback to confirm the call has fully entered the held state before trying to unhold. This avoids trying to unhold mid-state-transition.

内容的提问来源于stack exchange,提问作者Ishu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:24:08