loop-task

Troubleshooting

Common issues, FAQ, and debugging tips for loop-task.

Daemon won't start

The daemon starts automatically when you run any loop-task command, but sometimes it fails to launch. Here's how to diagnose it:

  1. Check if another daemon is already running - the daemon writes a PID file to ~/.loop-cli/daemon.pid. If that file exists and the process is still alive, a new daemon will refuse to start.

    cat ~/.loop-cli/daemon.pid
    ps -p $(cat ~/.loop-cli/daemon.pid)  # macOS/Linux
  2. Kill a stale process - if the PID file exists but the process is dead (zombie or crashed), remove it:

    kill $(cat ~/.loop-cli/daemon.pid) 2>/dev/null || true
    rm ~/.loop-cli/daemon.pid
  3. Check the daemon log - the daemon writes diagnostic output to ~/.loop-cli/daemon.log. This is the first place to look when something goes wrong:

    cat ~/.loop-cli/daemon.log
  4. Restart cleanly - loop-task restart kills any running daemon and starts a fresh one in one step.

Loop not running

A loop exists but never executes, or stops unexpectedly:

  1. Check status - loop-task status shows every loop and its current state. Look for the Status column.

  2. Paused or stopped - loops can be paused (loop-task pause <id>) or stopped (loop-task stop <id>). A paused loop retains its schedule and resumes where it left off; a stopped loop stays idle until started again.

    loop-task status --json | grep '"status"'
  3. Max runs reached - if a loop was created with --max-runs, it stops automatically after that many executions. Check the loop config:

    loop-task inspect <id>
  4. Check logs - each loop records its execution history. Fetch the last 50 log entries:

    curl http://127.0.0.1:8845/api/loops/<id>/logs?tail=50

    Or open the TUI board and select the loop to see its run history.

  5. Verify the command works - the most common silent failure is a command that works in your shell but not when run by the daemon. The daemon has no interactive shell, no aliases, and no PATH extensions from .bashrc/.zshrc. Run the command manually:

    # what loop-task actually runs (no shell wrapping):
    /bin/sh -c '<your command>'

Socket conflicts (macOS/Linux)

The daemon communicates with the CLI over a Unix socket at ~/.loop-cli/loop-task.sock.

  • Stale socket file - if the daemon crashes without cleaning up, the socket file stays on disk. Delete it and restart:

    rm ~/.loop-cli/loop-task.sock
    loop-task restart
  • Check what's using it - if another process holds the socket:

    lsof ~/.loop-cli/loop-task.sock
  • Port clash - if you changed the HTTP server port and something else is on that port, the daemon starts but the API is unreachable. Check with curl http://127.0.0.1:8845/api/health.

Windows named pipe issues

On Windows, the daemon uses a named pipe: \\.\pipe\loop-task.

  • Stuck pipe - if the daemon exits uncleanly, the pipe may remain in a half-open state. Use loop-task restart to force a clean reconnect.

  • Multiple processes - check Task Manager for leftover loop-task processes. End any that are unresponsive, then start fresh.

  • Permission denied - the named pipe is created with the current user's security context. If you run loop-task as a different user (e.g., via Run as Administrator), the pipe won't be visible to your normal user session. Use the same user consistently.

Log file permissions

All logs are stored in ~/.loop-cli/logs/.

  • Write access - ensure the user running the daemon has write permission to the ~/.loop-cli/ directory tree:

    ls -la ~/.loop-cli/
    chmod -R u+w ~/.loop-cli/logs   # macOS/Linux
  • Docker volumes - if running in a container, make sure the mounted volume has correct ownership. The daemon runs as the container's default user (often node or root). Check with:

    docker exec <container> ls -la /home/node/.loop-cli/logs
  • Disk full - the daemon logs silently. If the disk fills up, loops will fail without clear error messages. Check available space:

    df -h ~/.loop-cli/logs

Reset everything (nuclear option)

If loops, the daemon, or the data directory is in an unrecoverable state:

loop-task restart

If the daemon won't stop even with restart:

rm -rf ~/.loop-cli
loop-task start

This deletes all loops, tasks, projects, logs, and configuration. You will lose every loop you created. Only use this when nothing else works.

Debugging tips

WhatHow
Machine-readable statusloop-task status --json - parse with jq or any JSON tool
Daemon diagnosticscat ~/.loop-cli/daemon.log - timestamps every daemon action
Verbose loop creationloop-task new --verbose 30m -- npm test - see every step
Full state inspectioncurl http://127.0.0.1:8845/api/loops - raw JSON of all loops
API playgroundOpen http://127.0.0.1:8845/api/docs in your browser for Swagger UI
Watch live logstail -f ~/.loop-cli/daemon.log - follow new entries in real time
Environment snapshotloop-task env - dump PATH, working directory, and relevant env vars

FAQ

Do loops survive reboots?

Yes. All loop state is persisted to disk as JSON files in ~/.loop-cli/. When the daemon starts (manually or via loop-task), it reads the data directory, restores every loop, and accounts for any time that elapsed while the daemon was off. A loop scheduled every hour that missed two runs during a reboot will run immediately on daemon startup and then settle into its normal cadence.

Can I run multiple daemons?

No - only one daemon per machine is allowed. The PID file at ~/.loop-cli/daemon.pid prevents a second instance from starting. If you need isolated instances with separate data (e.g., for testing), set a different data directory:

LOOP_CLI_HOME=/tmp/my-test-instance loop-task start

This creates a completely separate environment with its own daemon, socket, and data files.

How do I export/import?

Export all loops, tasks, and projects to a portable JSON file:

loop-task export backup.json

Import from a backup:

loop-task import backup.json

Imports are additive - they merge with existing data. If a loop ID already exists, the import skips it unless you pass --force.

What happens if a command fails?

The loop captures the exit code and standard error, logs the failure, and waits for the next interval. It does not stop or self-destruct on failure - a failing loop keeps trying on schedule.

If you need failure handling, use task chaining with onFailureTaskId to trigger a recovery task or alert when a command exits non-zero.

Can I edit the JSON files directly?

Yes. All loop state is stored as plain JSON in ~/.loop-cli/loops/. The daemon uses a FileWatcher that detects external changes and hot-reloads them in real time. Just save the file and the daemon picks it up.

# Example: edit a loop's interval directly
code ~/.loop-cli/loops/<id>.json
# Change "interval": "30m" to "interval": "1h"
# Save - the daemon applies it immediately

Be careful: the daemon also writes to these files. Avoid editing while the daemon is actively running a loop to prevent race conditions. If in doubt, stop the loop first.

How does scheduling work?

Execution times are distributed across the interval using spread scheduling - the daemon computes hash(loopId) % interval to offset each loop within its window. This prevents the thundering herd problem where hundreds of loops scheduled every hour all fire at minute 0. See the Architecture docs for details.