VoIP.info Troubleshooting

Asterisk Gosub does not return or runs on the wrong channel

Trace one Gosub from invocation and stack frame through arguments, branch, Return, resumed priority and the caller or callee channel that owns its effects.

Reviewed 2026-10-14Asterisk subroutine documentation and Asterisk 22 application references reviewed October 12, 2026; exact application syntax, channel behavior and available functions remain dependent on the installed Asterisk branch and modules, and no PBX, dialplan, channel, stack frame, handler, endpoint, bridge, header, call or media path was inspected or testedAdvancedDocumentation based · not lab tested

Freeze one invocation and classify the first divergence

Record one authorized occurrence with timestamp and timezone, Asterisk branch, source and loaded dialplan, calling context, channel name, uniqueid, linked call identifier, Gosub target and arguments. Classify the first symptom: target not entered, wrong argument, expected branch skipped, no return, wrong resumed priority, effect on the wrong channel, or call failure after the subroutine completed.

Preserve logs and channel state before reloading or adding a blanket Return. A later hangup is not proof that the stack failed, and a visible variable is not proof that it belongs to the caller, callee or surviving channel.

Prove the loaded target and exact stack frame

Inspect the loaded context, extension, priority or label referenced by the invocation. Confirm that pattern selection, includes and conditional branches lead to that entry. Once inside, correlate STACK_PEEK() evidence with the expected caller frame and current channel.

A file containing the target does not prove that the running dialplan loaded it. A matching context name on another server, tenant or generated file is also insufficient. If the frame depth is not what the design expects, identify the additional nested call before changing return behavior.

Follow arguments and return data without borrowing stale state

Capture each argument as passed and its ARGn value in the selected frame. Distinguish absent, empty, normalized and rejected values. Then record every write intended for the caller and the Return() value expected in GOSUB_RETVAL.

Do not repair an empty argument by reading an unrelated global or inherited channel variable unless that ownership is part of the documented interface. A correct value in one frame does not prove that a nested frame or a different call received the same value.

Trace every branch to Return or an intentional exit

Walk the actual priority sequence for the failed input. Mark Goto, GosubIf, application failure, hangup and fall-through paths. Every path intended to resume the caller should reach one explicit Return; an intentional transfer of control or hangup should be documented as such.

After Return, verify the caller frame, channel and next priority. Do not treat a parsed dialplan, a single log message or a successful subroutine entry as evidence that the return occurred. Conversely, an expected hangup branch should not be changed merely to make the caller resume.

Identify whether the caller or callee channel owns the effect

For a plain Gosub, follow the current channel. For Dial() handlers, record whether B() ran on the caller before callee creation, b() ran on each created callee before dialing, or U() ran on the answered called channel before bridging. Then inspect the variable, header, identity or policy on that exact owner.

If a Local channel is present, include both halves and any optimization. If a handler wrote a PJSIP header, inspect the outbound PJSIP channel rather than a similarly named variable on the caller. Moving code between handler types can change call behavior and is not a safe generic fix.

Correct the first divergence and retest the full lifecycle

Correct only the demonstrated boundary: loaded target, argument construction, conditional branch, missing or misplaced Return, unintended nested frame, resumed priority, or wrong channel owner. Keep the previous dialplan and a supported rollback path.

Retest each return branch, missing and empty input, nested calls, caller and callee handler paths in scope, no-answer, answer, bridge and hangup cleanup. Verify the later call result separately from subroutine return. This workflow is documentation-derived and was not reproduced on an installed or laboratory Asterisk system.

Start at the restore and Macro-removal boundary on FreePBX 17

For a route that failed only after a FreePBX 17 restore, begin with the restored custom-macro diagnostic, then use this generic stack and channel workflow after the converted Gosub entry is proved.

Sources & applicability

Primary references for the technical details above. Operational examples and planning checklists are VoIP.info editorial guidance.

Examples require adaptation to your topology. No live PBX or hardware testing is claimed.