Troubleshooting Guide

Troubleshooting Guide

Comprehensive troubleshooting reference for the agentic-sandbox project.

SSH policy: Direct `ssh agent@...` commands in this guide are dev/break-glass diagnostics. They bypass gateway policy and audit guarantees. Managed-profile SSH should use the gateway-mediated access model described in `ADR-029` and the `SSH gateway rollout plan`.

Quick Diagnostics

Health Check Commands

# Management server health
curl http://localhost:8122/healthz

# Detailed health with metrics
curl http://localhost:8122/health

# Check readiness (requires at least one agent)
curl http://localhost:8122/ready

# List connected agents
curl http://localhost:8122/api/v1/agents

# View metrics
curl http://localhost:8122/metrics

# Check specific agent status
curl http://localhost:8122/api/v1/agents/<agent-id>

Status Verification

# Check VM status
virsh list --all

# Check VM IP and network
virsh domifaddr <vm-name>

# Dev/break-glass direct SSH: check if agent service is running inside VM
ssh agent@<vm-ip> systemctl status agentic-agent

# Dev/break-glass direct SSH: view agent logs
ssh agent@<vm-ip> journalctl -u agentic-agent -f

# Check management server logs
cd management && ./dev.sh logs

Log Locations

ComponentLocationCommand
Management serverstderr (dev mode)`./dev.sh logs`
Agent servicejournald`journalctl -u agentic-agent -f`
VM provisioning/var/log/cloud-init.log`ssh agent@<ip> cat /var/log/cloud-init.log`
Agentshare output/mnt/inbox/current`ls /mnt/inbox/current/`

Agent Connection Issues

Agent Not Connecting

SymptomPossible CauseSolution
Agent service inactiveBinary not deployedRun `./scripts/deploy-agent.sh <vm-name>`
"Connection refused"Management server downStart server: `cd management && ./dev.sh`
"Failed to connect to management server"Wrong IP in configCheck server IP is 192.168.122.1:8120
"Registration rejected"Secret mismatchVerify secret matches between agent and server
Agent connects then disconnectsgRPC stream errorCheck management server logs for errors

Diagnostic Steps:

# 1. Verify management server is running
curl http://localhost:8122/healthz
# Expected: "OK"

# 2. Check if VM can reach management server
ssh agent@<vm-ip> ping -c 3 192.168.122.1
# Expected: 0% packet loss

# 3. Test gRPC port connectivity
ssh agent@<vm-ip> nc -zv 192.168.122.1 8120
# Expected: "succeeded!"

# 4. Check agent service status
ssh agent@<vm-ip> systemctl status agentic-agent
# Expected: "active (running)"

# 5. View agent logs for connection errors
ssh agent@<vm-ip> journalctl -u agentic-agent -n 50 --no-pager

Agent Transport Identity Errors

Root Cause: The management server now requires transport-derived agent identity. Metadata-only legacy bearer authentication is rejected.

Error PatternCauseFix
"Agent transport identity required"Agent connected over plaintext TCP without UDS/vsock/mTLS identityReprovision with secure transport or bootstrap enrollment
"Transport identity does not match x-agent-instance-id"Agent metadata does not match the transport-bound identityReprovision the agent or repair the instance id binding
"mTLS URI-SAN identity required"Client certificate is missing the expected SPIFFE URI SANReissue agent mTLS material

Fix:

# Verify secure transport env exists.
ssh agent@<vm-ip> sudo grep 'AGENT_TRANSPORT\|AGENT_GRPC_TLS_' /etc/agentic-sandbox/agent.env

# Redeploy with secure transport material.
./scripts/deploy-agent.sh <vm-name>

Frequent Disconnects

Symptom: Agent connects successfully but drops connection every few minutes.

Possible CauseDiagnosticSolution
Heartbeat timeoutCheck agent logs for "heartbeat" messagesAgent sends heartbeat every 5s, server expects within 60s
Network issues`ping -c 100 192.168.122.1` from VMCheck for packet loss, latency spikes
Server overloadCheck server CPU/memoryScale down concurrent VMs or increase resources
gRPC keepalive timeoutCheck server logs for "keepalive"Server uses 10s keepalive, 20s timeout (see main.rs:172-174)

Fix Heartbeat Issues:

# Check heartbeat interval in agent logs
ssh agent@<vm-ip> journalctl -u agentic-agent | grep -i heartbeat

# Verify metrics are being sent
curl http://localhost:8122/api/v1/agents/<agent-id> | jq '.metrics'

# Increase heartbeat interval if network is slow
ssh agent@<vm-ip> sudo systemctl edit agentic-agent
# Add: Environment=HEARTBEAT_INTERVAL=10

Stale Agent Status

Symptom: Agent shows as "Stale" or "Disconnected" in dashboard despite being active.

Lifecycle: 1. Ready - Receiving regular heartbeats 2. Stale - No heartbeat for 60 seconds 3. Disconnected - Stale for 2 minutes 4. Removed - Disconnected for 5 minutes

Diagnostic:

# Check last heartbeat timestamp
curl http://localhost:8122/api/v1/agents/<agent-id> | jq '.last_heartbeat'

# Check agent status
curl http://localhost:8122/api/v1/agents/<agent-id> | jq '.status'

# Monitor heartbeat monitor logs
cd management && ./dev.sh logs | grep -i "stale\|heartbeat"

Fix:

# If agent is actually dead, remove it
virsh destroy <vm-name>
# Wait 5 minutes for automatic cleanup, or restart management server

# If agent is alive but marked stale, restart agent
ssh agent@<vm-ip> sudo systemctl restart agentic-agent

VM Issues

VM Won't Start

SymptomCauseSolution
`virsh start` fails with "domain not found"VM not definedRun provision script: `./images/qemu/provision-vm.sh <vm-name>`
"backing file not found"Base image missingBuild base image: `./images/qemu/build-base-image.sh ubuntu-24.04`
"insufficient permissions"User not in libvirt group`sudo usermod -aG libvirt $USER && newgrp libvirt`
VM starts but shuts down immediatelyCloud-init errorCheck `/var/log/cloud-init.log` inside VM
"Network not available"libvirt network downStart network: `virsh net-start default`

Diagnostic Steps:

# Check if VM is defined
virsh list --all | grep <vm-name>

# Check VM configuration
virsh dumpxml <vm-name> | less

# Verify base image exists
ls -lh /mnt/ops/base-images/ubuntu-24.04.qcow2

# Check libvirt network
virsh net-list
virsh net-info default

# View VM boot console (blocking command)
virsh console <vm-name>
# Press Ctrl+] to exit

VM Stuck in Provisioning

Symptom: VM starts but cloud-init never completes, no SSH access.

Common Causes:

  • Network not available during cloud-init
  • Cloud-init user-data syntax error
  • Disk full during setup
  • Missing dependencies in profile

Diagnostic:

# Attach to VM console (may require root password)
virsh console <vm-name>

# Check cloud-init status
cloud-init status

# View cloud-init logs
tail -100 /var/log/cloud-init.log
tail -100 /var/log/cloud-init-output.log

# Check if SSH is listening
ss -tlnp | grep :22

Recovery:

# Destroy stuck VM
virsh destroy <vm-name>
virsh undefine <vm-name>

# Clean up disk image
rm /var/lib/agentic-sandbox/vms/<vm-name>.qcow2

# Reprovision with minimal profile to test
./images/qemu/provision-vm.sh --profile basic <vm-name>

Cloud-init Failures

Symptom: VM boots but cloud-init reports errors, partial configuration.

Error PatternCauseFix
"failed to download package"Network timing issueAdd retries to apt commands in profile
"permission denied"File ownership wrongEnsure files created as correct user (agent)
"command not found"Dependency missingAdd dependency to earlier step in profile
"disk full"Profile too large for diskIncrease disk size: `--disk 60G`

Debug cloud-init:

# SSH into VM (if SSH works)
ssh agent@<vm-ip>

# Check cloud-init status
cloud-init status --long

# Rerun cloud-init module for testing
sudo cloud-init single --name cc_runcmd --frequency always

# View full cloud-init config
sudo cloud-init query userdata

# Check provisioning script output
cat /var/log/cloud-init-output.log | grep -A 10 -B 5 "ERROR\|FAILED"

Network Not Available

Symptom: VM has no network connectivity, can't reach 192.168.122.1.

DiagnosticCommandExpected Result
Check IP address`virsh domifaddr <vm-name>`Shows 192.168.122.2XX/24
Check from VM`ssh agent@<ip> ip addr`Shows IP on eth0/ens3
Ping gateway`ssh agent@<ip> ping -c 3 192.168.122.1`0% packet loss
Check DNS`ssh agent@<ip> ping -c 3 8.8.8.8`0% packet loss

Fix Network Issues:

# Restart libvirt network
virsh net-destroy default
virsh net-start default

# Check network configuration
virsh net-dumpxml default

# Restart VM networking inside VM
ssh agent@<ip> sudo systemctl restart systemd-networkd

# Verify DHCP lease
virsh net-dhcp-leases default

Management Service Watchdog

The packaged `agentic-mgmt.service` is a `Type=notify` unit with `WatchdogSec=30`, `KillMode=mixed`, and `LimitNOFILE=1048576`. The management process sends `READY=1` after the gRPC listener binds and the HTTP/WebSocket startup tasks have been launched, then sends `WATCHDOG=1` from a runtime task at half the configured watchdog interval.

Check the active unit contract:

systemctl show agentic-mgmt -p Type -p WatchdogUSec -p KillMode -p LimitNOFILE
systemctl status agentic-mgmt

Verify the running process inherited the descriptor limit:

pid=$(systemctl show agentic-mgmt -p MainPID --value)
grep 'Max open files' /proc/$pid/limits

If systemd restarts the service because of the watchdog, collect the journal and coredump before restarting again:

journalctl -u agentic-mgmt --since '30 minutes ago'
coredumpctl info agentic-mgmt

Look for `WATCHDOG=trigger`, `Watchdog timeout`, `SIGABRT`, and the last management log lines before the restart. A watchdog restart indicates the main Tokio runtime stopped scheduling the watchdog task; an HTTP self-watchdog exit indicates `/healthz/http` was unreachable for three consecutive probes while the runtime still scheduled the probe task.

SSH Connection Refused

Symptom: `ssh agent@<vm-ip>` fails with "Connection refused".

Possible CauseCheckFix
SSH not installedCheck cloud-init-output.logAdd to profile: `apt-get install -y openssh-server`
SSH not started`systemctl status sshd``sudo systemctl start ssh`
Wrong IP addressVerify IP with `virsh domifaddr`Use correct IP from libvirt
Cloud-init not finishedCheck `cloud-init status`Wait for completion (max 5 minutes)
Firewall blockingCheck iptables rulesDisable firewall: `sudo ufw disable`

Wait for SSH:

# Automated wait script
VM_IP="192.168.122.201"
for i in {1..60}; do
    if ssh -o ConnectTimeout=2 -o StrictHostKeyChecking=no agent@$VM_IP "echo ok" 2>/dev/null; then
        echo "SSH is ready!"
        break
    fi
    echo "Waiting for SSH... ($i/60)"
    sleep 5
done

Task Execution Issues

Task Stuck in PENDING

Symptom: Task created but never starts executing.

Possible CauseDiagnosticSolution
No agents available`curl http://localhost:8122/api/v1/agents`Start an agent VM
Agent doesn't match requirementsCheck task requirementsEnsure agent has required profile/labels
Dispatcher queue fullCheck server metricsRestart management server
Task assigned to disconnected agentCheck agent statusTask will timeout after 5 minutes

Diagnostic:

# Check task status
curl http://localhost:8122/api/v1/tasks/<task-id>

# List all agents
curl http://localhost:8122/api/v1/agents | jq '.[] | {id: .id, status: .status}'

# Check task queue
curl http://localhost:8122/metrics | grep agentic_tasks_pending

# View dispatcher logs
cd management && ./dev.sh logs | grep dispatcher

Task Stuck in RUNNING

Symptom: Task starts but never completes or times out.

Possible CauseCheckFix
Long-running processCheck task timeout configIncrease timeout or set to 0 (no timeout)
Process hungSSH to VM, check processesKill process: `pkill -9 <name>`
Output not streamingCheck agent logsAgent may have crashed
Hang detection not triggeredCheck hang detection configThreshold may be too high (default 10 min)

Diagnostic:

# Get task details
curl http://localhost:8122/api/v1/tasks/<task-id> | jq '.'

# Check command ID from task
COMMAND_ID=$(curl -s http://localhost:8122/api/v1/tasks/<task-id> | jq -r '.command_id')

# SSH to agent and check process
ssh agent@<vm-ip> ps aux | grep -v grep | grep <process>

# Check for zombie processes
ssh agent@<vm-ip> ps aux | grep Z

# Force kill if needed
ssh agent@<vm-ip> sudo pkill -9 -f <command>

Hang Detection Triggered

Symptom: Task automatically terminated with "hang detected" message.

Hang Detection Strategies:

  • Output Silence: No output for 10 minutes (default)
  • CPU Idle: CPU < 5% for 15 minutes
  • Process Stuck: No progress indicators for 20 minutes

Configuration:

# Check hang detection settings
curl http://localhost:8122/api/v1/tasks/<task-id>/hang-config

# Disable hang detection for long-running tasks
curl -X POST http://localhost:8122/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "command": "long-running-script.sh",
    "hang_detection": {
      "enabled_strategies": [],
      "recovery_action": "notify_only"
    }
  }'

Override for Specific Task:

// In task manifest YAML
hang_detection:
  output_silence_threshold_minutes: 30
  cpu_idle_threshold_minutes: 60
  recovery_action: preserve_for_debug

Artifact Collection Failed

Symptom: Task completes but artifacts are missing or incomplete.

Error PatternCauseSolution
"artifact path not found"Path doesn't exist in VMVerify path is correct, file was created
"permission denied"File not readable by agent userChange ownership: `chown agent:agent <file>`
"tar: file changed"File modified during collectionWait for process to finish before collecting
Artifact size is 0Glob pattern matched nothingCheck pattern syntax, use absolute paths

Manual Collection:

# SSH into VM
ssh agent@<vm-ip>

# Check if file exists
ls -lh /path/to/artifact

# Check permissions
stat /path/to/artifact

# Manually collect via scp
scp -r agent@<vm-ip>:/path/to/artifact ./local-artifacts/

# Check agentshare inbox
ls /mnt/inbox/<agent-id>/runs/*/outputs/

Timeout Exceeded

Symptom: Task killed after timeout period.

Default Timeouts:

  • Command timeout: Set per-command (0 = no timeout)
  • Task timeout: 1 hour default
  • Hang detection: 10 minutes output silence

Increase Timeout:

# For one-shot gRPC/agent commands, set the command timeout in that request.
# For orchestrated tasks, set lifecycle.timeout in the task manifest:
lifecycle:
  timeout: "2h"

Session Issues

Session Not Responding

Symptom: PTY session created but not responding to input.

DiagnosticCommandExpected
Check session exists`curl http://localhost:8122/api/v1/sessions`Session listed with status ACTIVE
Check from agent`ssh agent@<ip> ps aux \grep tmux`Process running
Test stdinSend test input via WebSocketShould echo back

Fix:

# Get session ID
SESSION_ID=$(curl -s http://localhost:8122/api/v1/sessions | jq -r '.sessions[0].id')

# Check session details
curl http://localhost:8122/api/v1/sessions/$SESSION_ID

# Send test input
wscat -c ws://localhost:8121/sessions/$SESSION_ID/attach
# Type: echo test

# If unresponsive, kill session
curl -X DELETE http://localhost:8122/api/v1/sessions/$SESSION_ID

PTY Not Working

Symptom: Session created but terminal features don't work (colors, cursor movement).

ProblemCauseFix
No colorsTERM not setSet `pty_term: "xterm-256color"`
Wrong dimensionsTerminal size not sentSend resize: `pty_cols: 80, pty_rows: 24`
Broken cursorLine discipline issueRestart session with fresh PTY
Echo disabledStdin/stdout mixed upCheck WebSocket frame types

Create Proper PTY Session:

curl -X POST http://localhost:8122/api/v1/sessions \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent-01",
    "session_name": "debug",
    "command": "/bin/bash",
    "allocate_pty": true,
    "pty_term": "xterm-256color",
    "pty_cols": 120,
    "pty_rows": 30
  }'

Session Orphaned After Restart

Symptom: After management server restart, PTY sessions still running on agent but not tracked by server.

Root Cause: Server state is not persisted. Session reconciliation handles cleanup.

Automatic Cleanup (Implemented):

When agent reconnects after server restart: 1. Server sends `SessionQuery` to agent 2. Agent reports all running sessions 3. Server sends `SessionReconcile` with sessions to kill 4. Agent terminates orphaned sessions

Manual Cleanup:

# List orphaned sessions on agent
ssh agent@<vm-ip> tmux list-sessions

# Kill specific session
ssh agent@<vm-ip> tmux kill-session -t main

# Kill all tmux sessions
ssh agent@<vm-ip> pkill -TERM tmux

# Check running_commands map
ssh agent@<vm-ip> journalctl -u agentic-agent -n 100 | grep "running_commands"

Reconciliation Failures

Symptom: Session reconciliation errors in logs, sessions not cleaned up.

Error PatternCauseFix
"Failed to kill session"Process already deadIgnore (harmless)
"Session not found"Race with process exitIgnore (already cleaned)
"Permission denied"Agent can't kill root processDon't run sessions as root
"Reconcile timeout"Agent didn't respondCheck agent connectivity

Debug Reconciliation:

# View reconciliation logs on server
cd management && ./dev.sh logs | grep -i reconcil

# View reconciliation on agent
ssh agent@<vm-ip> journalctl -u agentic-agent | grep -i reconcil

# Check for zombie sessions
ssh agent@<vm-ip> ps aux | grep Z

# Force clean state
ssh agent@<vm-ip> sudo pkill -KILL tmux
ssh agent@<vm-ip> sudo systemctl restart agentic-agent

Performance Issues

High CPU Usage

ComponentNormal RangeHigh ThresholdInvestigation
Management server5-15%> 50%Check active task count, gRPC connections
Agent5-20%> 80%Check running commands, metrics collection frequency
VM hypervisor (qemu)10-30% per VM> 60% per VMCheck workload, reduce concurrent VMs

Diagnostic:

# Server CPU usage
top -p $(pgrep agentic-management)

# Check active tasks
curl http://localhost:8122/metrics | grep agentic_tasks_running

# Agent CPU usage
ssh agent@<vm-ip> top -b -n 1

# Check metrics collection frequency
ssh agent@<vm-ip> journalctl -u agentic-agent | grep -i metrics | tail -20

Fix High CPU:

# Reduce heartbeat frequency (agent)
ssh agent@<vm-ip> sudo systemctl edit agentic-agent
# Add: Environment=HEARTBEAT_INTERVAL=10

# Reduce metrics collection frequency (server)
# Edit management/src/telemetry/metrics.rs, increase interval

# Limit concurrent VMs
# Max recommended: 4-6 VMs on 32GB host

Memory Exhaustion

Symptoms:

  • OOMKiller messages in dmesg
  • Slow response times
  • VMs failing to start

Memory Allocation:

ComponentTypical UsageLimit
Management server200-500 MB2 GB
Agent process50-100 MB1 GB
VM (agentic-dev)6-8 GB8 GB (default)
VM (basic)1-2 GB4 GB

Diagnostic:

# Check host memory
free -h
dmesg | grep -i oom

# Check VM memory limits
virsh dominfo <vm-name> | grep memory

# Monitor memory usage
watch -n 1 'free -h; echo; virsh list --all | grep running'

Fix Memory Issues:

# Reduce VM memory allocation
virsh setmem <vm-name> 4G --config
virsh shutdown <vm-name>
virsh start <vm-name>

# Or reprovision with lower memory
./images/qemu/provision-vm.sh --memory 4G <vm-name>

# Enable memory ballooning (dynamic allocation)
virsh dumpxml <vm-name> | grep balloon
# If not present, add memballoon device to VM XML

Disk Space Full

Common Locations:

PathUsageCleanup Strategy
`/var/lib/agentic-sandbox/vms/`VM disk imagesDelete unused VMs
`/mnt/ops/base-images/`Base imagesKeep only latest 2 versions
`/srv/agentshare/tasks/`Task artifactsArchive old tasks, set retention policy
`/srv/agentshare/inbox/`Agent outputsImplement log rotation

Diagnostic:

# Check disk usage
df -h

# Find large directories
du -h --max-depth=1 /var/lib/agentic-sandbox/ | sort -h

# Find large files
find /srv/agentshare -type f -size +100M -exec ls -lh {} \;

# Check VM disk usage
ssh agent@<vm-ip> df -h

Cleanup:

# Delete stopped VMs
for vm in $(virsh list --all --name | grep -v running); do
    virsh undefine $vm
    rm /var/lib/agentic-sandbox/vms/${vm}.qcow2
done

# Clean old task artifacts (older than 7 days)
find /srv/agentshare/tasks -type d -mtime +7 -exec rm -rf {} \;

# Clean old inbox runs (keep last 10 per agent)
for agent_dir in /srv/agentshare/inbox/*/runs; do
    ls -t $agent_dir | tail -n +11 | xargs -I {} rm -rf $agent_dir/{}
done

# Compact qcow2 images
qemu-img convert -O qcow2 old.qcow2 new.qcow2
mv new.qcow2 old.qcow2

Network Bottlenecks

Symptoms:

  • Slow artifact uploads
  • WebSocket frame drops
  • gRPC timeout errors

Diagnostic:

# Check network throughput
iftop -i virbr0

# Test transfer speed to VM
ssh agent@<vm-ip> "dd if=/dev/zero bs=1M count=100" | pv > /dev/null

# Check network latency
ping -c 100 192.168.122.201 | tail -1

# Monitor gRPC traffic
cd management && ./dev.sh logs | grep -i grpc

Optimization:

# Use virtiofs instead of 9p for agentshare (faster)
# Already implemented in provision-vm.sh

# Reduce output streaming frequency
# Batch stdout/stderr chunks on agent side

# Use compression for artifact collection
ssh agent@<vm-ip> tar czf - /path/to/artifacts | pv > artifacts.tar.gz

Management Server Issues

Server Won't Start

Error MessageCauseSolution
"Address already in use"Port 8120/8121/8122 in useKill existing process: `pkill agentic-management`
"Permission denied"Can't bind to privileged portUse ports > 1024 or run with sudo (not recommended)
"No such file or directory"Binary not built`cd management && cargo build --release`
"Secrets directory not found"Missing configCreate: `mkdir -p ~/.config/agentic-sandbox`
libvirt connection failedlibvirt not running`sudo systemctl start libvirtd`

Startup Checklist:

# 1. Check if process already running
pgrep agentic-management
# If found: kill -9 <pid>

# 2. Check port availability
ss -tlnp | grep -E "8120|8121|8122"

# 3. Verify secrets directory
ls -la ~/.config/agentic-sandbox/

# 4. Start server in dev mode
cd management
./dev.sh
# Watch for errors on startup

# 5. Test endpoints
curl http://localhost:8122/healthz

Libvirt RPC Degraded

Symptom: VM inventory or lifecycle endpoints return `503` with `LIBVIRT_UNRESPONSIVE`, while QEMU processes or agent heartbeats may still be alive.

Check:

curl -i http://localhost:8122/healthz/libvirt
timeout 5 virsh list --all
systemctl status libvirtd

The management API bounds read-only libvirt calls at 5 seconds and mutating VM lifecycle calls at 30 seconds. After repeated libvirt timeouts, the local circuit opens briefly and VM endpoints fail fast with `Retry-After` instead of piling more work into the blocking thread pool.

Recover:

sudo systemctl restart libvirtd
curl -i http://localhost:8122/healthz/libvirt

Port Already in Use

Symptom: Server fails to start with "bind: address already in use".

Find and Kill:

# Find process using port 8122 (HTTP)
sudo lsof -i :8122
# Or
ss -tlnp | grep 8122

# Kill the process
kill -9 <PID>

# For all server ports
for port in 8120 8121 8122; do
    sudo lsof -ti :$port | xargs -r kill -9
done

Change Ports (if needed):

# Edit management/src/main.rs
# Change DEFAULT_LISTEN_ADDR = "0.0.0.0:9120"

# Or use environment variable
LISTEN_ADDR="0.0.0.0:9120" ./dev.sh

gRPC Connection Refused

Symptom: Agent logs show "Failed to connect to management server".

CheckCommandFix
Server running`curl localhost:8122/healthz`Start server
Port listening`ss -tlnp \grep 8120`Check bind address
Firewall`sudo iptables -L \grep 8120`Allow port
TLS issuesCheck for certificate errorsWe use plaintext HTTP/2 (h2c)

Test gRPC Connectivity:

# From host
grpcurl -plaintext localhost:8120 list

# From VM
ssh agent@<vm-ip> nc -zv 192.168.122.1 8120

WebSocket Not Connecting

Symptom: Dashboard shows "WebSocket disconnected", can't stream output.

WebSocket Port: 8121 (HTTP port + 1)

Diagnostic:

# Test WebSocket connectivity
wscat -c ws://localhost:8121/health
# Expected: connection established

# Check if WebSocket server started
curl http://localhost:8122/healthz
# Then check logs for "Starting WebSocket server"

# Test from browser console
ws = new WebSocket("ws://localhost:8121/streams/test-123");
ws.onopen = () => console.log("Connected");

Common Fixes:

# 1. Wrong URL (use ws:// not http://)
# Correct: ws://localhost:8121/streams/<stream-id>
# Wrong: http://localhost:8121/streams/<stream-id>

# 2. Port blocked by firewall
sudo firewall-cmd --add-port=8121/tcp --permanent
sudo firewall-cmd --reload

# 3. WebSocket hub crashed
cd management && ./dev.sh restart

Storage Issues

virtiofs Mount Failed

Symptom: VM starts but `/mnt/inbox` or `/mnt/global` are empty.

Error in VMCauseFix
"mount: unknown filesystem type virtiofs"Kernel too oldUbuntu 22.04+ required
"mount.virtiofs: Connection refused"virtiofsd not runningCheck VM XML, restart VM
"Permission denied"SELinux/AppArmorDisable or configure policy

Diagnostic:

# Check if mount succeeded
ssh agent@<vm-ip> mount | grep virtiofs

# Check mount points
ssh agent@<vm-ip> ls /mnt/inbox /mnt/global

# Test writing to inbox
ssh agent@<vm-ip> touch /mnt/inbox/test.txt

# Check virtiofs in VM config
virsh dumpxml <vm-name> | grep virtiofs

Fix Mount Issues:

# Ensure agentshare directory exists on host
sudo mkdir -p /srv/agentshare/inbox/<agent-id>
sudo mkdir -p /srv/agentshare/global
sudo chown -R $USER:$USER /srv/agentshare

# Reprovision VM with agentshare
./images/qemu/provision-vm.sh --agentshare <vm-name>

# Manual mount inside VM (for testing)
ssh agent@<vm-ip> sudo mount -t virtiofs agentshare_inbox /mnt/inbox

Permission Denied

Symptom: Agent can't write to `/mnt/inbox`, gets "Permission denied".

virtiofs Permissions:

  • Mount tag must have correct permissions on host
  • Files created inside VM inherit host UID/GID
  • Recommended: Use UID=1000 (agent user) on both host and VM

Fix:

# Check ownership on host
ls -la /srv/agentshare/inbox/<agent-id>/

# Fix ownership (host side)
sudo chown -R $USER:$USER /srv/agentshare/

# Check ownership inside VM
ssh agent@<vm-ip> ls -la /mnt/inbox/

# Fix ownership (VM side)
ssh agent@<vm-ip> sudo chown -R agent:agent /mnt/inbox/

Disk Quota Exceeded

Symptom: "No space left on device" but `df -h` shows space available.

Cause: Disk quota enforced per VM (default: 40GB).

Check Quota:

# Inside VM
ssh agent@<vm-ip> df -h

# Check qcow2 image size
qemu-img info /var/lib/agentic-sandbox/vms/<vm-name>.qcow2

# Check actual disk usage
du -sh /var/lib/agentic-sandbox/vms/<vm-name>.qcow2

Increase Quota:

# Stop VM
virsh shutdown <vm-name>

# Resize qcow2 image
qemu-img resize /var/lib/agentic-sandbox/vms/<vm-name>.qcow2 +20G

# Start VM and resize filesystem
virsh start <vm-name>
ssh agent@<vm-ip> sudo growpart /dev/vda 1
ssh agent@<vm-ip> sudo resize2fs /dev/vda1

# Verify new size
ssh agent@<vm-ip> df -h /

Inbox Not Writable

Symptom: Agent can read from `/mnt/inbox` but can't create files.

Diagnostic:

# Test write access
ssh agent@<vm-ip> touch /mnt/inbox/writetest.txt
# Expected: No error

# Check mount options
ssh agent@<vm-ip> mount | grep inbox
# Should NOT show "ro" (read-only)

# Check permissions
ssh agent@<vm-ip> stat /mnt/inbox

Fix:

# Remount as read-write (inside VM)
ssh agent@<vm-ip> sudo mount -o remount,rw /mnt/inbox

# Or reprovision with correct mount flags
# Edit images/qemu/provision-vm.sh
# Ensure: rw,relatime in fstab entry

Security Issues

Secret Rotation Needed

When to Rotate:

  • Agent secret compromised
  • Periodic security policy (every 90 days)
  • Agent migration to new VM

Rotation Process:

# 1. Regenerate secure transport material
./images/qemu/provision-vm.sh <vm-name> --regenerate-transport

# 2. The script will:
#    - Generate new bootstrap or mTLS material
#    - Update /etc/agentic-sandbox/agent.env in VM
#    - Restart agent service

# 3. Verify new secret working
ssh agent@<vm-ip> journalctl -u agentic-agent -n 10
# Should show successful registration

# 4. Old secret automatically invalidated

Manual Rotation:

# Verify secure transport env, then restart after repair.
ssh agent@<vm-ip> sudo grep 'AGENT_TRANSPORT\|AGENT_GRPC_TLS_' /etc/agentic-sandbox/agent.env
ssh agent@<vm-ip> sudo systemctl restart agentic-agent

Certificate Errors

Note: Legacy plaintext TCP gRPC is no longer an authentication path for agents. Secure provisions use UDS, vsock, or mTLS transport identity.

If you see certificate errors, check:

# Verify connection is NOT using TLS
cd management && ./dev.sh logs | grep -i tls
# Should be empty

# Check agent connection string
ssh agent@<vm-ip> journalctl -u agentic-agent | grep "Connecting to"
# Should show: "host.internal:8120" or "192.168.122.1:8120"
# NOT: "https://" or "grpcs://"

Authentication Failures

Symptom: Agent rejected with "authentication failed".

Error PatternDiagnosticFix
"Agent transport identity required"Missing UDS/vsock/mTLS identityReprovision with secure transport
"Transport identity does not match x-agent-instance-id"Metadata/identity mismatchRepair instance id binding or reprovision
"mTLS URI-SAN identity required"Certificate lacks SPIFFE URI SANReissue agent certificate

Verify Authentication:

# Check agent sends identity metadata and uses secure transport
ssh agent@<vm-ip> journalctl -u agentic-agent | grep "x-agent-id\|x-agent-instance-id\|transport"

# Check server validates correctly
cd management && ./dev.sh logs | grep "authentication\|registration"

# Metadata-only grpcurl authentication is rejected; use the provisioned
# UDS/vsock/mTLS transport path for agent registration checks.

Diagnostic Commands Reference

Quick Health Check

#!/bin/bash
# comprehensive-health-check.sh

echo "=== Management Server ==="
curl -s http://localhost:8122/healthz && echo " (OK)" || echo " (FAILED)"

echo ""
echo "=== Connected Agents ==="
curl -s http://localhost:8122/api/v1/agents | jq -r '.[] | "\(.id): \(.status)"'

echo ""
echo "=== VMs Running ==="
virsh list --name

echo ""
echo "=== Agent Services ==="
for vm in $(virsh list --name); do
    IP=$(virsh domifaddr $vm 2>/dev/null | awk '/ipv4/ {print $4}' | cut -d/ -f1)
    if [ -n "$IP" ]; then
        STATUS=$(ssh -o ConnectTimeout=2 agent@$IP systemctl is-active agentic-agent 2>/dev/null)
        echo "$vm ($IP): $STATUS"
    fi
done

echo ""
echo "=== Disk Space ==="
df -h | grep -E "/$|/var|/srv"

echo ""
echo "=== Memory Usage ==="
free -h

Collect Debug Bundle

#!/bin/bash
# collect-debug-bundle.sh <agent-id>

AGENT_ID=$1
OUTPUT_DIR="debug-bundle-$(date +%Y%m%d-%H%M%S)"

mkdir -p $OUTPUT_DIR

# Server logs
cd management && cargo run > $OUTPUT_DIR/server.log 2>&1 &
sleep 5
kill $!

# Agent logs
VM_IP=$(virsh domifaddr $AGENT_ID | awk '/ipv4/ {print $4}' | cut -d/ -f1)
ssh agent@$VM_IP journalctl -u agentic-agent -n 500 > $OUTPUT_DIR/agent.log

# Configuration
virsh dumpxml $AGENT_ID > $OUTPUT_DIR/vm-config.xml
cp ~/.config/agentic-sandbox/agent-tokens $OUTPUT_DIR/
ssh agent@$VM_IP cat /etc/agentic-sandbox/agent.env > $OUTPUT_DIR/agent.env

# System info
curl -s http://localhost:8122/api/v1/agents/$AGENT_ID > $OUTPUT_DIR/agent-status.json
ssh agent@$VM_IP "df -h; free -h; ps aux" > $OUTPUT_DIR/system-info.txt

# Create tarball
tar czf debug-bundle.tar.gz $OUTPUT_DIR
rm -rf $OUTPUT_DIR
echo "Debug bundle: debug-bundle.tar.gz"

Log Analysis

Common Error Patterns

Log PatternMeaningAction
`Agent transport identity required`Authentication failureReprovision with secure transport or bootstrap enrollment
`Connection refused`Can't reach serverVerify server running, network OK
`Heartbeat timeout`Agent stopped sending heartbeatsCheck agent process, network latency
`Task timeout exceeded`Task ran too longIncrease timeout or optimize task
`Hang detected: output silence`No output for 10+ minutesCheck if process is actually hung
`Failed to collect artifact`Artifact path not foundVerify path exists, permissions OK
`virtiofs mount failed`Agentshare not mountedCheck virtiofsd, remount filesystem
`Session reconciliation failed`Can't clean up orphaned sessionManual cleanup with pkill

Correlation with Trace IDs

Trace IDs are used to correlate logs across components.

Example workflow:

# User creates task, receives task ID
TASK_ID="task-abc-123"

# Find associated command ID
COMMAND_ID=$(curl -s http://localhost:8122/api/v1/tasks/$TASK_ID | jq -r '.command_id')

# Search server logs for command
cd management && ./dev.sh logs | grep $COMMAND_ID

# Search agent logs for command
ssh agent@<vm-ip> journalctl -u agentic-agent | grep $COMMAND_ID

# Find related gRPC stream messages
cd management && ./dev.sh logs | grep "stream_id=$COMMAND_ID"

Recovery Procedures

Restart Services

# Restart management server
cd management && ./dev.sh restart

# Restart single agent
ssh agent@<vm-ip> sudo systemctl restart agentic-agent

# Restart all agents
for vm in $(virsh list --name); do
    IP=$(virsh domifaddr $vm | awk '/ipv4/ {print $4}' | cut -d/ -f1)
    [ -n "$IP" ] && ssh agent@$IP sudo systemctl restart agentic-agent
done

Recover a Soft-Locked Agent Without Restarting the Container

If the management server unregistered an agent while its container and tmux sessions are still alive — Inventory still shows the instance, but `GET /api/v1/agents/{id}/sessions` returns nothing and Cockpit can't attach (a "soft-lock") — reconnect it in place without destroying the running work:

# Docker agent: SIGHUP the running agent-client so it tears down the control
# stream, re-dials, and re-registers, re-adopting its existing tmux sessions.
# The container stays up and no running work is lost.
docker exec <container> agent-reconnect

# Equivalent (tini forwards the signal to agent-client):
docker kill --signal=HUP <container>

The agent re-registers and its sessions reappear:

curl -s http://localhost:8123/api/v1/agents/<agent-id>/sessions | jq

Prefer this over Restart Services above: restarting `agent-client` (`systemctl restart` / re-`exec`) stops the container and destroys the running tmux work, whereas `agent-reconnect` only re-establishes registration. The helper only signals the existing client — it never spawns a competing one.

Why a soft-lock is now rare (v2026.7.5): the stale-heartbeat reaper used

to remove an agent after ~300s without ingested heartbeats. It now defers to

control-stream liveness — a still-connected agent is kept registered even

when heartbeat ingestion lags (e.g. during a libvirt admin stall) — so a

live agent is no longer reaped out from under its sessions. `agent-reconnect`

is the operator lever for the residual cases.

Force-Kill Sessions

# Kill specific session by command ID
COMMAND_ID="cmd-abc-123"
curl -X POST http://localhost:8122/api/v1/sessions/$COMMAND_ID/kill

# Kill all sessions on agent
ssh agent@<vm-ip> sudo pkill -KILL tmux

# Clean up running_commands map (agent restart)
ssh agent@<vm-ip> sudo systemctl restart agentic-agent

Reprovision VM

# Full reprovision (destructive)
./scripts/destroy-vm.sh <vm-name>
./images/qemu/provision-vm.sh --profile agentic-dev --agentshare <vm-name>
./scripts/deploy-agent.sh <vm-name>

# Or use reprovision script (preserves name/IP)
./scripts/reprovision-vm.sh <vm-name> --profile agentic-dev

Clear Stuck Tasks

# List stuck tasks
curl http://localhost:8122/api/v1/tasks | jq '.[] | select(.status=="RUNNING")'

# Cancel task
curl -X POST http://localhost:8122/api/v1/tasks/<task-id>/cancel

# If cancel doesn't work, restart management server
cd management && ./dev.sh restart

AIWG Serve Integration Issues

Sandbox Not Registering

Symptom: No `Registered with aiwg serve` log line after startup.

# Check the endpoint is reachable
curl http://<AIWG_SERVE_ENDPOINT>/api/health

# Check the env var is set
./dev.sh logs | head -20
# Should show: aiwg serve not reachable at ... (will retry every 5 s)
# This is normal if aiwg serve hasn't started yet — registration retries automatically

The server retries registration every 5 seconds indefinitely. Start aiwg serve at any point and registration will complete on the next attempt.

Events Not Flowing to Dashboard

Symptom: Sandbox is registered but no agent/session events appear in aiwg serve dashboard.

# Check WebSocket connection in logs
./dev.sh logs | grep -i "aiwg serve WS"
# Should show: aiwg serve WS connected: ws://...

# If it shows repeated reconnects, check aiwg serve logs
# aiwg serve may be rejecting the token

# Verify sandbox token is valid
curl http://<AIWG_SERVE_ENDPOINT>/api/sandboxes/<sandbox-id> \
  -H "Authorization: Bearer <token>"

WebSocket Authentication Failure

Symptom: `aiwg serve WS` connection attempts fail immediately.

The management server receives a token from the registration response and passes it as `?token=<token>` on the WebSocket URL. If registration succeeded but WS fails:

1. Check aiwg serve logs for auth errors 2. Verify the management server time is in sync with aiwg serve (token expiry) 3. Restart the management server to force re-registration and a fresh token

HITL Requests Not Appearing in aiwg serve

Symptom: HITL requests appear in `GET /api/v1/hitl` locally but not in the aiwg serve dashboard.

# Confirm aiwg serve handle is wired in (check startup logs)
./dev.sh logs | grep -i "aiwg"

# Verify the hitl event type is listed in serve-guide event schema
# HITL events require aiwg serve v2026.4.0+
curl http://<AIWG_SERVE_ENDPOINT>/api/version

Standalone Mode Not Working After Removing AIWG Config

If you remove `AIWG_SERVE_ENDPOINT` and the server behaves unexpectedly:

# Confirm variable is gone from all config sources
./dev.sh stop
grep -r AIWG_SERVE management/.run/
./dev.sh

Getting Help

Diagnostic Information to Collect

When reporting issues, include:

1. Environment:

   uname -a
   cat /etc/os-release
   virsh version
   cargo --version

2. Server state:

   curl http://localhost:8122/health | jq
   curl http://localhost:8122/api/v1/agents | jq

3. Agent state:

   ssh agent@<vm-ip> systemctl status agentic-agent
   ssh agent@<vm-ip> journalctl -u agentic-agent -n 100 --no-pager

4. Logs:

  • Last 100 lines from management server
  • Last 100 lines from agent service
  • cloud-init logs if provisioning issue

Issue Tracker

Report issues at: https://github.com/jmagly/agentic-sandbox/issues

Issue Template:

## Environment

- OS: Ubuntu 24.04
- Rust version: 1.78.0
- Libvirt version: 10.0.0

## Reproduction Steps

1. Start management server
2. Provision VM with agentic-dev profile
3. Create PTY session
4. Observe error

## Expected Behavior

Session should remain responsive

## Actual Behavior

Session hangs after 5 minutes

## Logs

[Attach debug bundle or paste relevant logs]

## Additional Context

This started happening after upgrading libvirt

Appendix: Default Configuration

Management Server

SettingDefaultEnvironment Variable
gRPC address127.0.0.1:8120LISTEN_ADDR
WebSocket port8121(LISTEN_PORT + 1)
HTTP port8122(LISTEN_PORT + 2)
Secrets directory~/.config/agentic-sandboxSECRETS_DIR
Log levelinfoRUST_LOG
Heartbeat timeout60 seconds(hardcoded)
Stale cleanup300 seconds(hardcoded)

Agent

SettingDefaultEnvironment Variable
Server addresshost.internal:8120MANAGEMENT_SERVER
Heartbeat interval5 secondsHEARTBEAT_INTERVAL
Log levelinfoRUST_LOG
Log formatprettyLOG_FORMAT
Reconnect delay5 seconds(hardcoded)
Max reconnect delay60 seconds(hardcoded)

VM Resources

ProfileCPUsMemoryDiskBoot Time
basic22GB20GB~30s
agentic-dev48GB40GB~60s

Timeouts

OperationDefaultConfigurable
Command executionNo timeout (0)Per-command
Task execution1 hourPer-task
Hang detection (output silence)10 minutesPer-task
Hang detection (CPU idle)15 minutesPer-task
Hang detection (process stuck)20 minutesPer-task
Session reconciliation30 seconds(hardcoded)
Session kill grace period5 secondsPer-reconcile