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.
- Asterisk dialplan subroutines overview ↗
- Asterisk Gosub guide ↗
- Asterisk 22 Gosub application reference ↗
- Asterisk 22 Return application reference ↗
- Asterisk STACK_PEEK function reference ↗
- Asterisk pre-dial handlers ↗
- Asterisk pre-bridge handlers ↗
- Asterisk 22 Dial application reference ↗
- Sangoma: upgrading to FreePBX 17 ↗
- Sangoma: FreePBX 17 platform overview ↗
- Asterisk 20 Macro application reference ↗
- Asterisk Gosub application reference ↗
- Asterisk Return application reference ↗
- Asterisk LOCAL function reference ↗
- Asterisk LOCAL_PEEK function reference ↗
- Asterisk StackPop application reference ↗
Examples require adaptation to your topology. No live PBX or hardware testing is claimed.