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.
- Asterisk variable inheritance ↗
- Asterisk 22 Set application ↗
- Asterisk 22 CHANNEL function ↗
- Asterisk 22 MASTER_CHANNEL function ↗
- Asterisk 22 LOCAL function ↗
- Asterisk 22 LOCAL_PEEK function ↗
- Asterisk 22 VARIABLE_EXISTS function ↗
- Asterisk 22 IMPORT function ↗
- Asterisk 23 DIALPLAN_EXISTS function ↗
- Asterisk 23 EVAL_EXTEN function ↗
- Asterisk 23 EVAL_SUB function ↗
- Asterisk 23 Return application ↗
- Asterisk 23 PERIODIC_HOOK function ↗
- Asterisk 22 PERIODIC_HOOK function ↗
- Asterisk 20 PERIODIC_HOOK function ↗
- Asterisk Playback application ↗
- Asterisk CHANNEL function ↗
- Asterisk 23 WaitForSilence application ↗
- Asterisk 23 WaitForNoise application ↗
- Asterisk 22 WaitForSilence application ↗
- Asterisk 22 WaitForNoise application ↗
- Asterisk 23 BackgroundDetect application ↗
- Asterisk 23 WaitForCondition application ↗
- Asterisk 22 WaitForCondition application ↗
- Asterisk dialplan expressions ↗
- Asterisk conditional applications ↗
- Asterisk 23 VARIABLE_EXISTS function ↗
- Asterisk 23 GLOBAL_EXISTS function ↗
- Asterisk 23 WaitUntil application ↗
- Asterisk 22 WaitUntil application ↗
- Asterisk 23 TIMEOUT function ↗
- Asterisk 23 STRFTIME function ↗
- Asterisk 23 STRPTIME function ↗
- Asterisk 23 Dial application ↗
- Asterisk 22 Dial application ↗
- Asterisk pre-dial handlers ↗
- Asterisk hangup-cause overview ↗
- Asterisk HANGUPCAUSE_KEYS function ↗
- Asterisk 23 Originate application ↗
- Asterisk 22 Originate application ↗
- Asterisk 23 AMI Originate action ↗
- Asterisk ARI interface overview ↗
- Asterisk 23 Bridge application ↗
- Asterisk 22 Bridge application ↗
- Asterisk Bridge application overview ↗
- Asterisk 23 CHANNELS function ↗
- Asterisk ARI bridge overview ↗
- Asterisk 23 BridgeWait application ↗
- Asterisk 22 BridgeWait application ↗
- Asterisk 23 BridgeAdd application ↗
- Asterisk 22 BridgeAdd application ↗
- Asterisk 23 AMI Bridge action ↗
- Asterisk 22 AMI Bridge action ↗
- Asterisk 23 AMI BridgeList action ↗
- Asterisk 23 AMI BridgeInfo action ↗
- Asterisk 23 AMI BridgeKick action ↗
- Asterisk 22 AMI BridgeKick action ↗
- Asterisk 23 AMI BridgeCreate event ↗
- Asterisk 23 AMI BridgeEnter event ↗
- Asterisk 23 AMI BridgeLeave event ↗
- Asterisk 23 AMI BridgeInfoChannel event ↗
- Asterisk 23 AMI BridgeInfoComplete event ↗
- Asterisk HASH dialplan function ↗
- Asterisk HASHKEYS dialplan function ↗
- Asterisk ARRAY dialplan function ↗
- Asterisk ClearHash dialplan application ↗
- Asterisk SHIFT dialplan function ↗
- Asterisk channel-variable setting and substitution ↗
- Asterisk func_strings implementation ↗
- Asterisk CHANNEL_EXISTS dialplan function ↗
- Asterisk CLI channel creation and manipulation ↗
- Asterisk Local channel optimization ↗
- Asterisk AMI CoreShowChannels action ↗
- Asterisk AMI CoreShowChannel event ↗
- Asterisk func_channel implementation ↗
- Asterisk custom dynamic features ↗
- Asterisk 23 features.conf configuration reference ↗
- Asterisk built-in dynamic features ↗
- Asterisk features overview ↗
- Asterisk upstream features.conf sample ↗
- Asterisk upstream features configuration implementation ↗
- Asterisk 23 AMI Getvar action ↗
- Asterisk 23 AMI Setvar action ↗
- Asterisk 23 AMI VarSet event ↗
- Asterisk global-variable basics ↗
- Asterisk 23 SHARED dialplan function ↗
- Asterisk external-protocol dialplan-function safeguards ↗
- Asterisk AMI event filtering ↗
Examples require adaptation to your topology. No live PBX or hardware testing is claimed.