Master the root causes of no dialable leads errors and regain full campaign productivity through systematic list validation, hopper configuration, and database troubleshooting.
Prerequisites
Before troubleshooting, ensure you have:
- SSH access to your ViciDial server with sudo privileges
- Access to the ViciDial web admin panel at
/vicidial/admin.php - MySQL/MariaDB command-line access to the
asteriskdatabase - Understanding of basic ViciDial campaign structure (lists, dialers, agents)
- Root or asterisk-user permissions to check Asterisk processes
- Recent backups of your ViciDial database and configuration files
- A test campaign with known lead counts for validation
The "Campaign Has No Dialable Leads" error typically appears when:
- The dialer attempts to initiate calls but the hopper queue is empty
- All records in the lead list have been exhausted or marked as non-dialable
- List settings conflict with campaign configuration
- The database connection between ViciDial and the dialer is broken
- Lead filtering rules remove all records from the dialable pool
Understanding ViciDial List Architecture
The Lead Lifecycle in ViciDial
Every lead in ViciDial passes through status states that determine dialability. A lead is considered "dialable" if it matches specific criteria based on campaign and list configuration.
Lead Status States:
NEW— Fresh lead, never contactedQUEUE— Scheduled for dialingCALL— Currently being dialedLEFT MESSAGE— Voicemail was leftCALLED— Contacted but not completedXFER— Transferred to another departmentXFER SEND— Pending transferINCALL— Active call in progressCBHOLD— Callback hold statusCBSCHED— Callback scheduledDNCC— Do Not Call CompiledDNCL— Do Not Call List
The status field in the vicidial_list table controls whether a lead can be dialed again. Most campaigns set a maximum dial count limit to prevent redialing exhausted leads infinitely.
Hopper Mechanism
The ViciDial hopper is a real-time queue that feeds leads to available dialers. It operates by:
- Query: Dialer requests next available lead matching campaign criteria
- Fetch: System pulls record from
vicidial_listbased on filters - Lock: Lead is marked as
QUEUEto prevent duplicate dialing - Deliver: Lead is pushed to dialer for call attempt
- Release: After outcome, lead is updated with new status and dial count
If hopper returns zero results, the campaign has no dialable leads.
Prerequisites Validation Checklist
Verify ViciDial Installation
# Check ViciDial daemon status
systemctl status vicidial
# Verify Asterisk is running
asterisk -rx "core show version"
# Check MySQL/MariaDB connectivity
mysql -u cron -p -h localhost asterisk -e "SELECT VERSION();"
Confirm Database Connectivity
# Test asterisk database access
mysql -u root -p -e "SELECT * FROM asterisk.vicidial LIMIT 1;"
# Verify vicidial_list table exists
mysql -u root -p asterisk -e "DESCRIBE vicidial_list;"
Check ViciDial Core Files
# Confirm ViciDial admin path
ls -la /var/www/html/vicidial/admin.php
# Check CLI tools
ls -la /usr/share/astguiclient/
If any of these commands fail, address installation issues before proceeding with troubleshooting.
Systematic Troubleshooting Process
Step 1: Verify Lead List Exists and Has Records
Start with the most basic check—does your list actually contain leads?
# Log into MySQL
mysql -u root -p asterisk
# Check list summary
SELECT list_id, list_name, active, list_size,
COUNT(*) as record_count
FROM vicidial_lists
LEFT JOIN vicidial_list ON vicidial_lists.list_id = vicidial_list.list_id
WHERE list_id = 'YOUR_LIST_ID'
GROUP BY list_id;
Expected output: record_count should be greater than 0 and match list_size.
If record_count is 0, your list was never imported. Check:
# Look for import logs in ViciDial web admin: Admin > Import Lists
# Or check the command line import tool:
/usr/share/astguiclient/VICIDIAL_IN_GROUP.pl --list-id YOUR_LIST_ID
# Check ViciDial load queue
SELECT * FROM vicidial_list_queue LIMIT 5;
Step 2: Check Lead Status Distribution
The most common cause of "no dialable leads" is that all leads have been contacted and their status prevents redialing.
-- Check status breakdown for your list
SELECT list_id, status, COUNT(*) as count
FROM vicidial_list
WHERE list_id = 'YOUR_LIST_ID'
GROUP BY list_id, status
ORDER BY count DESC;
Expected output example:
+----------+--------+-------+
| list_id | status | count |
+----------+--------+-------+
| LIST-001 | CALLED | 4850 |
| LIST-001 | NEW | 150 |
| LIST-001 | QUEUE | 12 |
+----------+--------+-------+
If NEW count is very low or zero: Your leads have been exhausted or not imported with correct status. You need to reset or reimport.
Reset all leads to NEW status:
UPDATE vicidial_list
SET status = 'NEW',
dial_count = 0,
last_call_time = NULL
WHERE list_id = 'YOUR_LIST_ID'
LIMIT 10000;
-- Verify the update
SELECT COUNT(*) FROM vicidial_list
WHERE list_id = 'YOUR_LIST_ID' AND status = 'NEW';
Step 3: Validate Campaign Configuration
Campaign settings directly control which leads are considered dialable.
# Access the ViciDial web interface
# Navigate: Admin > Campaigns > [Your Campaign]
# OR query directly:
mysql -u root -p asterisk -e "SELECT * FROM vicidial_campaigns WHERE campaign_id = 'YOUR_CAMPAIGN' \G"
Key fields to check:
SELECT campaign_id,
active,
dial_method,
list_id,
max_dial_count,
auto_dial_level,
lead_filter_id,
campaign_script_id,
calls_in_queue_limit
FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
Critical fields:
| Field | Expected Value | Issues |
|---|---|---|
active |
Y |
If N, campaign is disabled |
dial_method |
ADAPT or MANUAL |
Wrong method blocks dialing |
list_id |
Your list ID | Blank or wrong list blocks dialing |
max_dial_count |
Usually 3-5 | Too low exhausts leads quickly |
auto_dial_level |
1-9 | 0 disables auto-dialing |
lead_filter_id |
Usually empty | Filters can exclude all leads |
Step 4: Check List Configuration
# Via web admin: Admin > Lead Lists > [Your List]
# Or via MySQL:
mysql -u root -p asterisk -e "SELECT * FROM vicidial_lists WHERE list_id = 'YOUR_LIST_ID' \G"
Critical fields:
SELECT list_id, list_name, active, list_size,
project_area, script_id, lead_filter_id,
campaign_id_string
FROM vicidial_lists
WHERE list_id = 'YOUR_LIST_ID';
Check if list is flagged active:
UPDATE vicidial_lists
SET active = 'Y'
WHERE list_id = 'YOUR_LIST_ID';
Step 5: Examine Hopper Queue in Real-Time
The hopper is where leads are staged immediately before dialing. Check if it's populated:
-- Check active hopper entries
SELECT campaign_id, count(*) as hopper_count
FROM vicidial_hopper
WHERE campaign_id = 'YOUR_CAMPAIGN'
GROUP BY campaign_id;
-- View sample hopper records
SELECT hopper_id, campaign_id, status, priority, dialable_status
FROM vicidial_hopper
WHERE campaign_id = 'YOUR_CAMPAIGN'
LIMIT 10;
If hopper is empty: The dialer cannot find leads matching campaign criteria.
Manually populate hopper (for testing):
# Use the ViciDial CLI tool to load hopper
/usr/share/astguiclient/VICIDIAL_hopper_loader.pl --campaign-id YOUR_CAMPAIGN --queue-size 100
Check the Asterisk log for hopper loading output:
tail -50 /var/log/asterisk/messages | grep -i hopper
Step 6: Validate Dialer Group Configuration
Dialers are assigned to groups. Check if your campaign's dialer group can access the list.
-- Find dialer group for your campaign
SELECT campaign_id, dialer_group_id
FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
-- Check dialer group members
SELECT dialier_id, dialer_group, active, server_ip, count_attempt_leads
FROM vicidial_dialiers
WHERE dialer_group = 'YOUR_DIALER_GROUP'
AND active = 'Y';
For each dialer, verify it's reachable:
# Test dialer connectivity
asterisk -rx "sip show peers" | grep "YOUR_DIALER"
# Check dialer status in ViciDial logs
tail -100 /var/log/asterisk/messages | grep -i "dialier"
Step 7: Check Lead Filter Rules
Lead filters can silently exclude all records from dialability.
-- Identify filters assigned to your campaign
SELECT lead_filter_id FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
-- View filter rules
SELECT lead_filter_id, filter_name, filter_type, filter_label
FROM vicidial_lead_filters
WHERE lead_filter_id IN (
SELECT lead_filter_id FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN'
);
-- Check filter criteria
SELECT * FROM vicidial_lead_filter_rules
WHERE lead_filter_id = 'YOUR_FILTER_ID';
Temporarily disable filter to test:
UPDATE vicidial_campaigns
SET lead_filter_id = ''
WHERE campaign_id = 'YOUR_CAMPAIGN';
-- Restart campaign in web admin
If leads become dialable after removing the filter, the filter rules are the culprit. Review and fix filter criteria.
Step 8: Review Dial Count vs. Max Dial Count
Each lead has a dial_count field. If it meets or exceeds max_dial_count, it becomes non-dialable.
-- Check dial count distribution
SELECT list_id, dial_count, COUNT(*) as count
FROM vicidial_list
WHERE list_id = 'YOUR_LIST_ID'
GROUP BY dial_count
ORDER BY dial_count DESC;
-- Check max_dial_count for campaign
SELECT campaign_id, max_dial_count
FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
If all leads have dial_count >= max_dial_count:
-- Reset dial count
UPDATE vicidial_list
SET dial_count = 0
WHERE list_id = 'YOUR_LIST_ID';
-- Or increase max_dial_count for campaign
UPDATE vicidial_campaigns
SET max_dial_count = 10
WHERE campaign_id = 'YOUR_CAMPAIGN';
Common Causes and Specific Solutions
Cause 1: List Not Linked to Campaign
-- Verify campaign points to correct list
SELECT campaign_id, list_id, list_id_string, dial_method
FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
-- Fix: Update campaign to use correct list
UPDATE vicidial_campaigns
SET list_id = 'YOUR_LIST_ID',
list_id_string = 'YOUR_LIST_ID'
WHERE campaign_id = 'YOUR_CAMPAIGN';
Cause 2: Campaign Paused or Auto-Dial Disabled
-- Check campaign active status and auto-dial level
SELECT campaign_id, active, auto_dial_level, campaign_status
FROM vicidial_campaigns
WHERE campaign_id = 'YOUR_CAMPAIGN';
-- Restart campaign
UPDATE vicidial_campaigns
SET active = 'Y',
auto_dial_level = 4,
campaign_status = 'ACTIVE'
WHERE campaign_id = 'YOUR_CAMPAIGN';
Then restart ViciDial:
systemctl restart vicidial
Cause 3: All Leads Marked as Do Not Call
-- Check for DNCC (Do Not Call Compiled) status
SELECT list_id, status, COUNT(*) as count
FROM vicidial_list
WHERE list_id = 'YOUR_LIST_ID'
AND status IN ('DNCC', 'DNCL')
GROUP BY status;
-- Clear DNCC marks (if list was incorrectly flagged)
UPDATE vicidial_list
SET status = 'NEW'
WHERE list_id = 'YOUR_LIST_ID'
AND status = 'DNCC';
Cause 4: Asterisk-ViciDial Communication Broken
Check if Asterisk is responding to ViciDial requests:
# Check Asterisk process
ps aux | grep asterisk | grep -v grep
# Restart Asterisk
systemctl restart asterisk
# Check SIP connectivity
asterisk -rx "sip show peers"
# Tail Asterisk logs
tail -100 /var/log/asterisk/messages
Cause 5: Database Corruption or Lock
# Check for locked tables
mysql -u root -p asterisk -e "SHOW OPEN TABLES WHERE In_use > 0;"
# Restart MySQL if locked
systemctl restart mariadb
# Verify vicidial_list table integrity
mysql -u root -p asterisk -e "CHECK TABLE vicidial_list;"
# Repair if corrupted
mysql -u root -p asterisk -e "REPAIR TABLE vicidial_list;"
Advanced Configuration Examples
Complete Hopper Loading Script
Create /usr/local/bin/load_hopper.sh:
#!/bin/bash
CAMPAIGN_ID="$1"
QUEUE_SIZE="$2"
if [ -z "$CAMPAIGN_ID" ] || [ -z "$QUEUE_SIZE" ]; then
echo "Usage: $0 <CAMPAIGN_ID> <QUEUE_SIZE>"
exit 1
fi
# Load hopper using Perl CLI
/usr/share/astguiclient/VICIDIAL_hopper_loader.pl \
--campaign-id "$CAMPAIGN_ID" \
--queue-size "$QUEUE_SIZE" \
--priority 5 \
--dialable-status READY
# Verify hopper loaded
HOPPER_COUNT=$(mysql -u root -p'PASSWORD' asterisk -N -e \
"SELECT COUNT(*) FROM vicidial_hopper WHERE campaign_id='$CAMPAIGN_ID';")
echo "Hopper loaded: $HOPPER_COUNT records"
# Log event
echo "$(date): Hopper loaded for $CAMPAIGN_ID with $HOPPER_COUNT records" \
>> /var/log/vicidial_hopper.log
Usage:
chmod +x /usr/local/bin/load_hopper.sh
/usr/local/bin/load_hopper.sh YOUR_CAMPAIGN 500
List Import with Status Validation
When importing new lists, ensure correct status assignment:
#!/bin/bash
# Script: import_list_with_validation.sh
LIST_FILE="$1"
LIST_ID="$2"
CAMPAIGN_ID="$3"
# Import list using ViciDial importer
/usr/share/astguiclient/VICIDIAL_IN_GROUP.pl \
--list-id "$LIST_ID" \
--list-file "$LIST_FILE" \
--delete-duplicates YES \
--ignore-blank-phone YES
# Validate import
TOTAL=$(mysql -u root -p'PASSWORD' asterisk -N -e \
"SELECT COUNT(*) FROM vicidial_list WHERE list_id='$LIST_ID';")
NEW_COUNT=$(mysql -u root -p'PASSWORD' asterisk -N -e \
"SELECT COUNT(*) FROM vicidial_list WHERE list_id='$LIST_ID' AND status='NEW';")
echo "List $LIST_ID imported with $TOTAL total records, $NEW_COUNT in NEW status"
# Verify campaign is linked
mysql -u root -p'PASSWORD' asterisk -e \
"UPDATE vicidial_campaigns SET list_id='$LIST_ID' WHERE campaign_id='$CAMPAIGN_ID';"
echo "Campaign $CAMPAIGN_ID linked to list $LIST_ID"
Campaign Diagnostic Script
Create /usr/local/bin/diagnose_campaign.sh:
#!/bin/bash
# Complete campaign diagnostics
CAMPAIGN_ID="$1"
DB_USER="root"
DB_PASS="your_password"
DB_NAME="asterisk"
echo "=== ViciDial Campaign Diagnostics ==="
echo "Campaign: $CAMPAIGN_ID"
echo "Time: $(date)"
echo ""
# 1. Campaign status
echo "--- CAMPAIGN STATUS ---"
mysql -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT campaign_id, campaign_name, active, auto_dial_level,
dial_method, list_id, max_dial_count, dialer_group_id
FROM vicidial_campaigns WHERE campaign_id='$CAMPAIGN_ID';"
# 2. List statistics
echo ""
echo "--- LEAD LIST STATUS ---"
LIST_ID=$(mysql -u $DB_USER -p$DB_PASS $DB_NAME -N -e \
"SELECT list_id FROM vicidial_campaigns WHERE campaign_id='$CAMPAIGN_ID';")
mysql -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT list_id, status, COUNT(*) as count FROM vicidial_list
WHERE list_id='$LIST_ID' GROUP BY status ORDER BY count DESC;"
# 3. Hopper status
echo ""
echo "--- HOPPER STATUS ---"
mysql -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT campaign_id, COUNT(*) as hopper_count FROM vicidial_hopper
WHERE campaign_id='$CAMPAIGN_ID' GROUP BY campaign_id;"
# 4. Dialer status
echo ""
echo "--- DIALER STATUS ---"
DIALER_GROUP=$(mysql -u $DB_USER -p$DB_PASS $DB_NAME -N -e \
"SELECT dialer_group_id FROM vicidial_campaigns WHERE campaign_id='$CAMPAIGN_ID';")
mysql -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT dialier_id, dialer_group, active, server_ip FROM vicidial_dialiers
WHERE dialer_group='$DIALER_GROUP';"
# 5. Active calls
echo ""
echo "--- ACTIVE CALLS ---"
mysql -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT COUNT(*) as active_calls FROM vicidial_log
WHERE campaign_id='$CAMPAIGN_ID' AND call_date >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)
AND end_epoch = 0;"
# 6. Asterisk SIP peers
echo ""
echo "--- ASTERISK SIP PEERS ---"
asterisk -rx "sip show peers" | head -5
echo ""
echo "=== END DIAGNOSTICS ==="
Usage:
chmod +x /usr/local/bin/diagnose_campaign.sh
/usr/local/bin/diagnose_campaign.sh YOUR_CAMPAIGN
Troubleshooting Checklist
| Issue | Diagnostic Command | Fix |
|---|---|---|
| No leads in list | SELECT COUNT(*) FROM vicidial_list WHERE list_id='LIST_ID'; |
Re-import list |
| All leads exhausted | SELECT status, COUNT(*) FROM vicidial_list WHERE list_id='LIST_ID' GROUP BY status; |
Reset status to NEW |
| Campaign not linked to list | SELECT list_id FROM vicidial_campaigns WHERE campaign_id='CAMP_ID'; |
Update campaign list_id |
| Hopper empty | SELECT COUNT(*) FROM vicidial_hopper WHERE campaign_id='CAMP_ID'; |
Run hopper loader script |
| Campaign paused | SELECT active FROM vicidial_campaigns WHERE campaign_id='CAMP_ID'; |
Set active='Y' |
| Dial count exceeded | SELECT dial_count FROM vicidial_list WHERE list_id='LIST_ID' GROUP BY dial_count; |
Reset dial_count or increase max_dial_count |
| Filter blocking leads | SELECT lead_filter_id FROM vicidial_campaigns WHERE campaign_id='CAMP_ID'; |
Remove or fix filter |
| Asterisk down | systemctl status asterisk |
systemctl restart asterisk |
| Database locked | SHOW OPEN TABLES WHERE In_use > 0; |
systemctl restart mariadb |
| Dialer group offline | SELECT active FROM vicidial_dialiers WHERE dialer_group='GROUP'; |
Check dialer connectivity |
Asterisk CLI Verification
Once you've fixed database issues, verify Asterisk can communicate:
# Check Asterisk is running
asterisk -rx "core show version"
# Monitor SIP registrations
asterisk -rx "sip show peers"
# Watch active calls
asterisk -rx "core show calls"
# Monitor channel activity
asterisk -rx "core show channels"
# Check ViciDial queues
asterisk -rx "queue show"
# View call statistics
asterisk -rx "core show stats"
# Monitor in real-time (attach to console)
asterisk -r
# Then type: dialplan show vicidial-set-agent-status (or relevant context)
ViciDial Restart Procedure
After making configuration changes, properly restart ViciDial and Asterisk:
# Stop ViciDial services
systemctl stop vicidial
systemctl stop asterisk
# Optional: Check for hanging processes
ps aux | grep -E "(asterisk|vicidial)" | grep -v grep
# Kill any remaining processes
killall -9 asterisk
killall -9 perl
# Start services in correct order
systemctl start mariadb
sleep 5
systemctl start asterisk
sleep 10
systemctl start vicidial
# Verify all services started
systemctl status vicidial asterisk mariadb
# Check logs for errors
tail -50 /var/log/asterisk/messages
tail -50 /var/log/vicidial.log
Monitoring and Prevention
Set Up Real-Time Campaign Monitoring
-- Create monitoring query to run periodically
-- Check for campaigns with empty hoppers
SELECT c.campaign_id, c.campaign_name,
COALESCE(COUNT(h.hopper_id), 0) as hopper_count,
(SELECT COUNT(*) FROM vicidial_list
WHERE list_id = c.list_id AND status = 'NEW') as new_leads
FROM vicidial_campaigns c
LEFT JOIN vicidial_hopper h ON c.campaign_id = h.campaign_id
WHERE c.active = 'Y'
GROUP BY c.campaign_id
HAVING hopper_count < 50 OR new_leads = 0;
Enable ViciDial Debug Logging
Edit /etc/asterisk/extensions-vicidial.conf:
[vicidial-set-agent-status]
exten => _X.,1,Set(VERBOSE_PREFIX_3=***VICIDIAL_DEBUG***)
exten => _X.,n,NoOp(Campaign: ${CAMPAIGN_ID} | List: ${LIST_ID} | Lead: ${LEAD_ID})
exten => _X.,n,NoOp(Dialable: ${DIALABLE_STATUS} | Status: ${LEAD_STATUS})
Then in Asterisk CLI:
asterisk -rx "core set debug 9"
asterisk -rx "core set verbose 9"
asterisk -rx "sip set debug on"
Alert on Low Hopper Conditions
Create a cron job to alert when hopper is depleted:
#!/bin/bash
# File: /usr/local/bin/check_hopper_alert.sh
THRESHOLD=100
MAIL="[email protected]"
for CAMPAIGN in $(mysql -u root -p'PASSWORD' asterisk -N -e \
"SELECT campaign_id FROM vicidial_campaigns WHERE active='Y';"); do
HOPPER=$(mysql -u root -p'PASSWORD' asterisk -N -e \
"SELECT COUNT(*) FROM vicidial_hopper WHERE campaign_id='$CAMPAIGN';")
if [ "$HOPPER" -lt "$THRESHOLD" ]; then
echo "WARNING: Campaign $CAMPAIGN hopper count: $HOPPER" | \
mail -s "ViciDial Hopper Alert: $CAMPAIGN" "$MAIL"
fi
done
Add to crontab:
*/5 * * * * /usr/local/bin/check_hopper_alert.sh
Summary
The "Campaign Has No Dialable Leads" error in ViciDial results from systematic issues across list management, campaign configuration, and database state. Follow this troubleshooting path:
- Verify leads exist — Count records in
vicidial_listtable - Check lead status — Ensure NEW leads are available, not CALLED/DNCC
- Validate campaign config — Confirm list linkage, active status, auto-dial enabled
- Inspect hopper — Verify hopper has entries ready for dialers
- Review filters — Check lead filters aren't excluding all records
- Confirm dialers — Ensure dialer group is active and connected
- Test Asterisk — Verify SIP registration and channel availability
- Restart services — Properly restart ViciDial, Asterisk, and MySQL in sequence
Use the provided diagnostic scripts and SQL queries to isolate the root cause in your specific environment. Most issues resolve through resetting lead status to NEW, properly linking campaigns to lists, or clearing problematic filters. For production systems, implement the monitoring scripts to prevent recurrence.