VoIP.info Guide

Asterisk channel-variable scope and inheritance

Choose channel, child-depth, Gosub-frame, master-channel or explicit cross-channel scope before using a variable to control a call.

Reviewed 2026-10-19Current Asterisk variable-inheritance guidance and Asterisk 22 Set, CHANNEL, MASTER_CHANNEL, LOCAL, LOCAL_PEEK, VARIABLE_EXISTS and IMPORT documentation reviewed October 15, 2026; channel creation, inheritance depth, Gosub stack frames, master-channel lifetime, cross-channel access and loaded dialplan remain deployment specific; no PBX, channel, variable, subroutine, transfer, Local-channel path or live call was inspected or testedAdvancedDocumentation based · not lab tested

Name the channel that owns the value

A normal channel variable belongs to the channel on which it is set. Caller, callee, both Local-channel halves, a transfer-created leg and a channel running a pre-dial handler can share one call story while retaining different variable stores. Record CHANNEL(name), CHANNEL(channeltype), CHANNEL(uniqueid) and CHANNEL(linkedid) at the read and write points.

CHANNEL() returns an empty string for an item unavailable on the current channel. Treat empty as an observation to classify, not automatic proof that an upstream value was never sent or set.

Apply one-level and recursive inheritance deliberately

Asterisk documents _NAME as inheritance to a channel created from the current channel with the prefix removed, so it travels one child level. __NAME keeps the double prefix on the child and continues through descendants. The copy occurs when the child is created; adding a prefix after creation does not retroactively update that child.

Prefixed and unprefixed forms are references to the same variable name for setting purposes. Setting any form removes the other forms, so a later Set(NAME=...) can change both the value and future inheritance intent. Keep the exact write order and child-creation point visible.

Keep Gosub-local values inside their stack frame

LOCAL(name) creates a value local to the current Gosub stack frame. When Return() removes that frame, the value disappears or the previously hidden value becomes visible again. It is not a child-channel inheritance mechanism.

LOCAL_PEEK(n,name) reads through a specified number of local frames; zero means the current level, and exceeding the available frames returns an empty string. Use it only when crossing a stack-frame boundary is part of the subroutine contract, not as a general search for a value with an unknown owner.

Use master-channel access only for the documented owner

MASTER_CHANNEL(name) accesses the oldest associated channel while that channel still exists. Asterisk identifies that master through the channel linked ID; when the current channel is the master or the master no longer exists, the function accesses local channel variables instead.

A shared linked ID therefore does not make all variables shared, and master-channel disappearance can change where an expression reads or writes. Do not use MASTER_CHANNEL() as durable storage, a lock, a transfer-proof global or a substitute for an explicitly owned data record.

Make cross-channel reads and missing values explicit

IMPORT(channel,name) retrieves a variable from a named channel. Preserve the exact channel name and prove it still exists at the read point; names, legs and lifetimes can change through dialing, transfers and Local-channel optimization. Avoid constructing a channel name from untrusted input.

VARIABLE_EXISTS(name) distinguishes an existing channel variable from one that does not exist. Keep existence separate from a blank stored value, a value hidden by a Gosub frame, a variable on another leg and an unavailable CHANNEL() item.

Test every creation boundary and restore the original scope

For one authorized flow, log correlation-safe channel identifiers, the write priority, exact variable form, child-creation event, read priority, existence result and redacted value class. Exercise one child, one grandchild only when recursion is intended, each Local half, the relevant Gosub return and any transfer or fork that creates another leg.

Correct the earliest wrong owner, prefix, timing, stack frame or explicit channel reference. Then remove temporary logging, verify secrets never entered logs and retest an unrelated route. No PBX, channel, variable, subroutine, transfer, Local-channel path or live call was tested for this guide.

Record caller and target scope during lookup

The dialplan lookup diagnostic checks target-context variable resolution rather than assuming the caller extension survives evaluation.

Keep periodic hook variables attached to the correct channel

The periodic-hook diagnostic distinguishes stored owner-channel state from HOOK_CHANNEL and HOOK_ID on the asynchronous hook channel.

Read a wait result on the channel that ran the application

The silence and noise diagnostic preserves the exact channel, immediate status value and later branch instead of transferring a result across call legs by assumption.

A condition can only observe the state owner it actually reads

The WaitForCondition diagnostic distinguishes current-channel variables, inherited values, globals and explicitly shared state before changing timing.

Capture wait status on the channel that ran the application

The WaitUntil diagnostic treats a later read or another channel as different evidence.

Read Dial results on their owning caller channel

The Dial diagnostic captures status and timing immediately, then handles caller and callee state separately.

Set and read Originate values on the intended channel

The Originate diagnostic keeps caller-owned status, destination v() variables and later Dial state separate.

Read Bridge state on its owning channel

The Bridge diagnostic keeps caller-owned BRIDGERESULT, target-owned BRIDGE_NOANSWER and surviving-channel exit state separate.

Read BRIDGERESULT on the BridgeAdd caller

The holding and add diagnostic keeps result state attached to the executing channel.

Do not treat manager acknowledgement as a dialplan variable

The AMI bridge-control guide keeps action correlation and channel-local result state separate.

Apply channel scope to structured dialplan state

The HASH and ARRAY guide treats a hash name, key snapshot and ARRAY targets as state whose actual channel and child-creation timing must be proved.

Attach variables to the intended channel identity

The channel identity guide records the full live leg and Local optimization boundary before interpreting channel-variable state.

Trace DYNAMIC_FEATURES across channel creation

The custom dynamic features guide distinguishes one- and two-underscore inheritance from explicit per-leg authorization.

Apply the same scope contract to AMI

The AMI variable guide shows why omitting Channel means global scope and why inheritance is fixed when a child is created.

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.