← All Tutorials

ViciDial "Campaign Has No Dialable Leads" — List & Hopper Troubleshooting

ViciDial Administration Intermediate 15 min read #74

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:

The "Campaign Has No Dialable Leads" error typically appears when:

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:

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:

  1. Query: Dialer requests next available lead matching campaign criteria
  2. Fetch: System pulls record from vicidial_list based on filters
  3. Lock: Lead is marked as QUEUE to prevent duplicate dialing
  4. Deliver: Lead is pushed to dialer for call attempt
  5. 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:

  1. Verify leads exist — Count records in vicidial_list table
  2. Check lead status — Ensure NEW leads are available, not CALLED/DNCC
  3. Validate campaign config — Confirm list linkage, active status, auto-dial enabled
  4. Inspect hopper — Verify hopper has entries ready for dialers
  5. Review filters — Check lead filters aren't excluding all records
  6. Confirm dialers — Ensure dialer group is active and connected
  7. Test Asterisk — Verify SIP registration and channel availability
  8. 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.

Stuck on something specific?

Book a free 30-minute call. I run ViciDial centers across 3 countries and can usually unblock your setup in one session — or build it for you.

Book a Free Consultation