# Common LoopX Error Messages and Solutions: A Complete Troubleshooting Guide

> Troubleshoot common LoopX errors with this complete guide. Learn solutions for validation failures, file issues, quota violations, and security breaches to keep your project running smoothly.

- Repository: [huangruiteng/loopx](https://github.com/huangruiteng/loopx)
- Tags: troubleshooting-guide
- Published: 2026-08-15

---

**Common LoopX errors fall into four categories—validation failures, file/path issues, quota accounting violations, and sandbox security breaches—and most can be resolved by supplying missing flags, correcting paths, or adjusting configuration parameters.**

LoopX is a modular orchestration framework that enforces strict contracts at runtime. When those contracts are violated, the system raises explicit exceptions with descriptive messages tied directly to source locations. This guide catalogs the most frequently encountered LoopX error messages, traces each to its origin in the codebase, and provides actionable solutions with runnable examples.

## Validation Errors in State Refresh Operations

The [`state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/state_refresh.py) module handles the majority of input validation for LoopX's core refresh-state workflow. These errors typically indicate missing required arguments or invalid combinations of flags.

### "next_action must not be empty"

This error surfaces when a refresh-state command is invoked without specifying what action should follow.

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 165](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L165)

**Triggering example:**

```bash
loopx refresh-state --goal my-goal

# ValueError: next_action must not be empty

```

**Solution:**

```bash
loopx refresh-state --goal my-goal --next-action run

```

### "state file is required when the goal is not resolvable from registry"

LoopX distinguishes between registry-based goals and ad-hoc goals. Non-registry goals require explicit state file paths.

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 404](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L404)

**Triggering example:**

```bash
loopx refresh-state --goal ./local-goal.yaml --next-action run

# ValueError: state file is required when the goal is not resolvable from registry

```

**Solution:**

```bash
loopx refresh-state --goal ./local-goal.yaml --state-file ./state.json --next-action run

```

### "relative state file requires --project or registry repo"

Relative paths for state files need context to resolve correctly.

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 407](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L407)

**Solution:** Use absolute paths or supply `--project`:

```bash
loopx refresh-state --state-file state.json --project my-project --next-action run

```

### "--agent-lane requires --agent-id so the lane has an owner"

Agent lanes in LoopX represent isolated execution contexts that must be owned by a specific agent.

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 845](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L845)

**Triggering example:**

```bash
loopx refresh-state --agent-lane production --next-action run

# ValueError: --agent-lane requires --agent-id so the lane has an owner

```

**Solution:**

```bash
loopx refresh-state --agent-id 123e4567-e89b-12d3-a456-426614174000 \
                    --agent-lane production --next-action run

```

### "turn-scoped refresh-state has no settlement identity"

This error occurs when attempting to refresh state for a turn that hasn't completed its settlement phase.

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 889](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L889)

**Solution:** Ensure the turn has executed and generated a settlement record before calling refresh-state.

## File and Path Errors

LoopX implements strict path containment rules to prevent directory traversal attacks and ensure reproducible builds.

### "state file does not exist: <path>"

**Source location:** [[`loopx/state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py) line 973](https://github.com/huangruiteng/loopx/blob/main/loopx/state_refresh.py#L973)

**Triggering example:**

```python
from loopx.state_refresh import RefreshState

refresher = RefreshState(state_file="/nonexistent/state.json")

# FileNotFoundError: state file does not exist: /nonexistent/state.json

```

**Solution:**

```python
import os
from loopx.state_refresh import RefreshState

# Verify before instantiating

state_path = "/tmp/state.json"
if not os.path.exists(state_path):
    with open(state_path, "w") as f:
        f.write("{}")

refresher = RefreshState(state_file=state_path)

```

### "escapes project root"

LoopX validates that all paths remain within the project boundary.

**Source location:** [[`tests/test_state_file_containment.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_state_file_containment.py) line 62](https://github.com/huangruiteng/loopx/blob/main/tests/test_state_file_containment.py#L62)

**Triggering example:**

```bash
loopx refresh-state --state-file ../../../etc/passwd --next-action run

# PathError: escapes project root

```

**Solution:** Use `os.path.normpath` to sanitize inputs or restrict to project-relative paths:

```python
import os

def sanitize_project_path(user_input, project_root):
    """Ensure path stays within project root."""
    full_path = os.path.normpath(os.path.join(project_root, user_input))
    if not full_path.startswith(os.path.normpath(project_root)):
        raise ValueError("Path escapes project root")
    return full_path

```

### "must not contain symlinks"

For reproducibility, static sites cannot include symbolic links.

**Source location:** [[`tests/test_static_site_presentation.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py) line 169](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py#L169)

**Solution:** Replace symlinks with actual file copies before validation.

### "reserved path"

Certain paths (`.git`, `.github`, internal directories) are reserved and cannot be written to.

**Source location:** [[`tests/test_static_site_presentation.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py) line 164](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py#L164)

## Quota and Resource Accounting Errors

The [`slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/slot_accounting.py) module enforces LoopX's quota system, tracking resource consumption across goals and agents.

### "status payload does not include runtime_root"

Quota accounting requires runtime context to attribute resource usage correctly.

**Source location:** [[`loopx/control_plane/quota/slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py) line 893](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py#L893)

**Solution:** Include `runtime_root` in status payloads:

```python
from loopx.status import StatusPayload

payload = StatusPayload(
    runtime_root="/var/run/loopx/agent-123",
    # ... other fields

)
quota_accountant.record(payload)

```

### "quota slot spend requires an eligible preview"

Slot spending is only permitted after generating a valid preview.

**Source location:** [[`loopx/control_plane/quota/slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py) line 686](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py#L686)

**Workflow fix:**

```bash

# First, generate preview

loopx quota preview --goal my-goal --slots 5 > preview.json

# Then spend with the preview

loopx quota spend --preview preview.json --slots 5

```

### "quota slot spend source must be one of: ..."

The `source` parameter is restricted to a whitelist of identifiers.

**Source location:** [[`loopx/control_plane/quota/slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py) line 689](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py#L689)

**Valid sources:** `api`, `cli`, `webhook`, `automation`

**Triggering example:**

```bash
loopx quota spend --source manual --slots 5

# ValueError: quota slot spend source must be one of: api, cli, webhook, automation

```

**Solution:**

```bash
loopx quota spend --source cli --slots 5

```

### "after.spent_slots must equal before.spent_slots + slots"

This invariant violation indicates a race condition or accounting bug.

**Source location:** [[`loopx/control_plane/quota/slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py) line 699](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py#L699)

**Solution:** Audit concurrent quota operations; implement locking if multiple processes spend from the same quota pool.

### "goal id is required" and path traversal variants

Goal identifiers must be simple, single-segment strings without directory traversal.

**Source locations:** [[`loopx/control_plane/quota/slot_accounting.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py) lines 165-169](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/quota/slot_accounting.py#L165-L169)

**Triggering examples:**

```bash
loopx quota spend --slots 5

# ValueError: goal id is required

loopx quota spend --goal "../etc/config" --slots 5

# ValueError: goal id must be a single path segment

loopx quota spend --goal "valid-goal/../escape" --slots 5

# ValueError: must not include path traversal

```

**Solution:** Use alphanumeric identifiers with hyphens or underscores:

```bash
loopx quota spend --goal "my-valid-goal-123" --slots 5

```

## Sandbox and Security Errors

LoopX's sandbox layer enforces execution constraints for worker safety.

### "unsafe shell metacharacters"

Worker commands undergo strict validation to prevent shell injection.

**Source location:** [[`tests/test_worker_command_validation.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_worker_command_validation.py) line 44](https://github.com/huangruiteng/loopx/blob/main/tests/test_worker_command_validation.py#L44)

**Triggering example:**

```bash
loopx worker run --command "curl https://example.com | sh"

# ValueError: unsafe shell metacharacters

```

**Metacharacters blocked:** `|`, `&`, `;`, `$`, `` ` ``, `{`, `}`, `<`, `>`, `*`, `?`

**Solution:** Pass arguments explicitly rather than shell pipelines:

```bash
loopx worker run --command "curl" --arg "https://example.com"

# Download and process separately through LoopX's task composition

```

### "non-root sandbox user"

Certain sandbox operations require root privileges for namespace management.

**Source location:** [[`tests/test_skillsbench_verifier_cache.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_verifier_cache.py) line 46](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_verifier_cache.py#L46)

**Solution:** Run with appropriate privileges or configure the sandbox for unprivileged operation mode.

## Installation and Configuration Errors

### "unsupported fixed LoopX entry host surface"

Slash commands must target supported host surfaces.

**Source location:** [[`tests/test_slash_command_install.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_slash_command_install.py) line 90](https://github.com/huangruiteng/loopx/blob/main/tests/test_slash_command_install.py#L90)

**Solution:** Consult [`slash_commands.py`](https://github.com/huangruiteng/loopx/blob/main/slash_commands.py) for the whitelist of supported surfaces and target accordingly.

### "public boundary scan failed"

Static site assets must satisfy visibility constraints.

**Source location:** [[`tests/test_static_site_presentation.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py) line 159](https://github.com/huangruiteng/loopx/blob/main/tests/test_static_site_presentation.py#L159)

**Common causes:** Hidden files, restricted permissions, or assets outside the public directory.

## Infrastructure and Runtime Errors

### "Docker compose command failed"

**Source location:** [[`tests/test_skillsbench_setup_preflight.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_setup_preflight.py) line 1002](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_setup_preflight.py#L1002)

**Diagnostic checklist:**

- Verify Docker daemon is running: `docker info`
- Check compose file syntax: `docker-compose config`
- Review container logs: `docker-compose logs`

### "api unavailable"

Network failures to external services like GitHub.

**Source location:** [[`tests/test_pr_review_github_scan.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_pr_review_github_scan.py) line 90](https://github.com/huangruiteng/loopx/blob/main/tests/test_pr_review_github_scan.py#L90)

**Resolution steps:**

1. Verify network connectivity
2. Check token validity and permissions
3. Review GitHub status page for outages
4. Implement retry logic with exponential backoff

## Private Error Detail Containment

LoopX maintains strict boundaries around internal error details in certain contexts.

### "PRIVATE_TIMEOUT_DETAIL_SHOULD_NOT_ESCAPE"

**Source location:** [[`tests/test_skillsbench_automation_loop_timeouts.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_automation_loop_timeouts.py) line 118](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_automation_loop_timeouts.py#L118)

Indicates a timeout occurred in a private loop context where details should not propagate. **Resolution:** Adjust timeout configuration or fix the underlying stall condition.

### "PRIVATE_FAILURE_DETAIL_MUST_NOT_PROJECT"

**Source location:** [[`tests/test_skillsbench_reverse_tunnel_supervisor.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_reverse_tunnel_supervisor.py) line 415](https://github.com/huangruiteng/loopx/blob/main/tests/test_skillsbench_reverse_tunnel_supervisor.py#L415)

Signals a supervisor failure detail leaked past abstraction boundaries. **Resolution:** Audit error handling in supervisor code to ensure proper encapsulation.

## Summary

- **Validation errors** in LoopX typically require adding missing flags like `--agent-id`, `--next-action`, or `--state-file`, or correcting path references to stay within project boundaries.
- **Quota errors** follow a strict preview-then-spend workflow; always generate a preview before spending slots and use whitelisted source identifiers.
- **Security errors** enforce command safety through metacharacter blocking and path containment—sanitize inputs and avoid shell constructs.
- **File errors** demand verified, absolute paths with proper permissions; handle `FileNotFoundError` by pre-validating state file existence.
- **Sandbox errors** may require privilege adjustments or configuration changes depending on the execution environment.

## Frequently Asked Questions

### What causes "next_action must not be empty" in LoopX?

The `refresh-state` command requires an explicit `--next-action` parameter to determine workflow continuation. Without it, the validation in [`state_refresh.py`](https://github.com/huangruiteng/loopx/blob/main/state_refresh.py) line 165 rejects the command. Supply a valid action like `run`, `pause`, or `terminate`.

### How do I fix "quota slot spend requires an eligible preview"?

LoopX enforces a two-phase quota process: preview generation validates that requested resources are available, and spending commits the allocation. Generate a preview first with `loopx quota preview`, then reference that preview when calling `loopx quota spend`. The preview record ensures atomicity and prevents overspending.

### Why does LoopX reject paths with symlinks or directory traversal?

Reproducibility and security drive these constraints. Symlinks create nondeterministic build environments, while `../` traversals could expose sensitive files. LoopX validates paths in [`test_static_site_presentation.py`](https://github.com/huangruiteng/loopx/blob/main/test_static_site_presentation.py) and [`test_state_file_containment.py`](https://github.com/huangruiteng/loopx/blob/main/test_state_file_containment.py) to guarantee hermetic execution. Replace symlinks with copies and normalize paths to project-relative locations.