Dial()
"No devices or endpoints to dial" in Asterisk
This one has a nasty property: the line that fails is not the line with the bug. Dial() ran with nothing to dial because a variable was empty, and Asterisk logs no warning at the point where the variable was set.
What it looks like in the log
[2026-08-17 10:58:12] VERBOSE[6620][C-00000ff1] pbx.c: Executing [s@outbound:8] Dial("PJSIP/1002-000012f4", "PJSIP/@trunk-main,30") in new stack
[2026-08-17 10:58:12] VERBOSE[6620][C-00000ff1] app_dial.c: No devices or endpoints to dial
What it means
Look closely at the dial string in the log line: PJSIP/@trunk-main. There is nothing between the slash and the at sign. A variable that was supposed to hold the number resolved to an empty string, and Asterisk cheerfully built a dial string around the nothing it was given.
The reason this is hard to find is that setting a variable to an empty value is completely legal in Asterisk and produces no warning at all. The first visible symptom appears here, one or more steps after the actual mistake, and pointing at the wrong line.
Most common causes
-
01A database or ODBC lookup that returned no rows
The subtle case. A lookup that fails outright usually logs something. A lookup that succeeds and returns zero rows sets the variable to empty and logs nothing, and that empty value flows downstream looking exactly like this.
-
02An AGI script that did not set the variable
A script that exits early, hits an unhandled branch, or writes to the wrong variable name leaves the expected one unset.
-
03A misspelled variable name
Referencing
${DEST}where the value was stored in${DESTINATION}yields an empty string silently. There is no undefined-variable error to catch this. -
04A header or channel variable that was not present
Reading a SIP header that this particular call did not carry gives an empty result, so the failure appears only on the subset of calls missing that header.
-
05A pattern match that captured nothing
A dialplan pattern whose capture group did not match produces an empty substitution rather than failing the match.
How to tell which one it is
The dial string in the log tells you exactly which part of it was empty by its shape. PJSIP/@trunk means the number was empty. PJSIP/12025550143@ means the trunk name was. /1234 with no technology means the tech variable was.
From there, trace backwards through this call's dialplan execution to the Set(), AGI() or function call that was supposed to populate it. The value is not visible in the log, but the step that should have produced it is, and checking whether that step ran at all usually answers the question.
How to fix it
-
Guard the dial with an emptiness check
A
GotoIf($["${DEST}" = ""]?nodest)before the Dial converts a silent failure into a deliberate branch you can log and route. -
Distinguish empty from failed in lookups
Check the row count or return status of a database lookup explicitly rather than trusting that a set variable means a successful one.
-
Log the variable at the point it is set
A
NoOpprinting the value immediately after the lookup makes the next occurrence trivially diagnosable, and costs nothing. -
Check the variable name against where it was written
Compare the name used in the Dial against the name used in the Set. This is the fastest thing to rule out and a frequent answer.