Macro to Gosub Migration
Macro to Gosub Migration
Old Asterisk dialplans often use Macro() for reusable call logic. New dialplans should use Gosub() instead. Gosub() is clearer, nests correctly, returns explicitly, and is the supported pattern after app_macro was removed in Asterisk 21. Asterisk 20 and older releases may still load it, but it is deprecated and should be migrated.
Macro pattern
A classic macro might look like this:
[macro-dial-user]
exten => s,1,NoOp(Dialing ${ARG1})
same => n,Dial(PJSIP/${ARG1},20)
same => n,MacroExit()
[internal]
exten => _1XX,1,Macro(dial-user,${EXTEN})
same => n,Hangup()
Inside a macro, arguments are exposed as ${ARG1}, ${ARG2}, and so on. The macro always starts at s,1 in a context named macro-name.
Gosub equivalent
[sub-dial-user]
exten => s,1,NoOp(Dialing ${ARG1})
same => n,Dial(PJSIP/${ARG1},20)
same => n,Return()
[internal]
exten => _1XX,1,Gosub(sub-dial-user,s,1(${EXTEN}))
same => n,Hangup()
The arguments still appear as ${ARG1}, ${ARG2}, and so on. The difference is that the destination is explicit: context, extension, priority, and optional argument list.
Migration rules
| Macro | Gosub |
|---|---|
Macro(name,arg1,arg2) |
Gosub(sub-name,s,1(arg1,arg2)) |
[macro-name] |
[sub-name] or another normal context name |
MacroExit() |
Return() |
${MACRO_EXTEN} |
Pass ${EXTEN} as an argument when needed |
${MACRO_CONTEXT} |
Pass ${CONTEXT} as an argument when needed |
${MACRO_PRIORITY} |
Pass priority only if the subroutine really needs it |
Before and after example
; Before
[macro-record-and-dial]
exten => s,1,MixMonitor(${UNIQUEID}.wav)
same => n,Dial(PJSIP/${ARG1},${ARG2})
same => n,MacroExit()
[internal]
exten => _1XX,1,Macro(record-and-dial,${EXTEN},25)
same => n,Hangup()
; After
[sub-record-and-dial]
exten => s,1,MixMonitor(${UNIQUEID}.wav)
same => n,Dial(PJSIP/${ARG1},${ARG2})
same => n,Return()
[internal]
exten => _1XX,1,Gosub(sub-record-and-dial,s,1(${EXTEN},25))
same => n,Hangup()
Common mistakes
- Forgetting
Return(), which causes dialplan execution to continue into whatever follows the subroutine. - Using
MacroExit()in a Gosub context. - Relying on
${MACRO_EXTEN}without passing${EXTEN}into the subroutine. - Naming subroutine contexts
macro-..., which confuses future readers even if it still works as a context name.
See Also
User Notes
Know a tip or gotcha for this topic? Share it below and help others.
Contribute a note
Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.