Error Codes
Reference for all Nefia structured error codes.
Nefia uses a structured error code system to help you quickly diagnose and resolve problems. Every error includes a code in the format Exxxx, where the first digit indicates the category.
| Prefix | Category |
|---|---|
| E1xxx | VPN |
| E2xxx | SSH |
| E3xxx | SFTP / Filesystem |
| E4xxx | Configuration |
| E5xxx | Policy |
| E6xxx | Authentication |
| E7xxx | Execution |
| E8xxx | Team |
| E81xx | Security / Device / Posture |
| E82xx | Secrets / Cluster |
| E9xxx | Internal |
Using nefia explain
You can look up any error code from the terminal with the nefia explain command. This displays the title, a detailed description, step-by-step resolution instructions, and a link to the relevant documentation page.
nefia explain E2001Error [E2001]: SSH authentication failed
The SSH key or credentials were rejected by the remote host.
Resolution:
- Verify the SSH key: ssh -i ~/.ssh/id_ed25519 <user>@<host>
- Check key file permissions: chmod 600 ~/.ssh/id_ed25519 (Linux/macOS) or use icacls to restrict access (Windows)
- Ensure the public key is in the remote host's ~/.ssh/authorized_keys.
- Re-enroll the host if SSH keys were rotated: nefia vpn reinvite --host <host>
To list all registered error codes at once:
nefia explain --listCODE TITLE E1001 VPN connection failed E1002 VPN key invalid E1003 VPN enrollment failed E1004 VPN setup failed E1005 DERP relay connection failed E1006 DERP relay authentication failed E1007 Enrollment rate limited E2001 SSH authentication failed E2002 SSH connection failed E2003 Command timed out E2004 SSH session failed E2005 SSH host key changed E3001 File not found E3002 Permission denied E3003 File too large E3004 SFTP operation failed E3005 Path denied by policy E4001 Invalid configuration E4002 Invalid selector E4003 Host not found E5001 Command denied by policy E5002 License limit exceeded E6001 Authentication required E6002 Authentication token expired E6003 Device limit exceeded E7001 Command exited with non-zero status E7002 Invalid playbook E8001 Team not found E8002 Insufficient team permissions E8003 Team member limit exceeded E8004 Invalid invitation code E8101 Device lock denied E8102 Posture check denied E8201 Secret resolution failed E8202 Cluster node is not the leader E8301 Sudo not enabled E8302 Sudo command denied E9001 Internal error E9002 Operation cancelled E9003 Schedule not found E9004 Queue entry not found E9005 Queue invalid state E9006 Job not found
VPN Errors (E1xxx)
| Code | Title | Description |
|---|---|---|
| E1001 | VPN connection failed | The WireGuard VPN tunnel could not be established or has been disrupted. |
| E1002 | VPN key invalid | The WireGuard public or private key is malformed or does not match the peer configuration. |
| E1003 | VPN enrollment failed | The invite token was rejected, has expired, or has already been used. |
| E1004 | VPN setup failed | The WireGuard VPN interface could not be configured on the local machine. |
| E1005 | DERP relay connection failed | Could not connect to the DERP relay server. The relay server may be down, the URL may be incorrect, or a firewall may be blocking WebSocket connections. |
| E1006 | DERP relay authentication failed | The DERP relay server rejected the authentication handshake. The WireGuard private key may be invalid, or the server's ACL may not include this client's public key. |
| E1007 | Enrollment rate limited | Too many enrollment requests were sent from the same IP address. The operator rate-limits enrollment to prevent abuse. |
E1001 — VPN connection failed
The WireGuard VPN tunnel could not be established or has been disrupted.
| Step | Resolution |
|---|---|
| 1 | Check that the target host is powered on and connected to the internet. |
| 2 | Verify the VPN endpoint is reachable: nefia vpn diagnose --host <host>. |
| 3 | Ensure the operator and agent clocks are synchronized (WireGuard requires less than 2 minutes of skew). |
| 4 | Re-enroll the host if keys may have changed: nefia vpn reinvite --host <host>. |
E1002 — VPN key invalid
The WireGuard public or private key is malformed or does not match the peer configuration.
| Step | Resolution |
|---|---|
| 1 | Regenerate the VPN keys: nefia vpn rotate-key. |
| 2 | Re-enroll the affected host: nefia vpn reinvite --host <host>. |
| 3 | Check that the private key in the config is a valid base64-encoded 32-byte key. |
E1003 — VPN enrollment failed
The invite token was rejected, has expired, or has already been used.
| Step | Resolution |
|---|---|
| 1 | Generate a new invite token: nefia vpn invite --name <host> --os <os>. |
| 2 | Check that the token has not expired (default: 24 hours). |
| 3 | Ensure the token has not been used by another host (tokens are single-use). |
E1004 — VPN setup failed
The WireGuard VPN interface could not be configured on the local machine.
| Step | Resolution |
|---|---|
| 1 | Check that no other VPN or WireGuard instance is using the same listen port (default: 51820). |
| 2 | Verify the VPN address range does not conflict with existing network interfaces. |
| 3 | Review the VPN configuration: nefia validate. |
| 4 | Check firewall rules allow UDP traffic on the WireGuard listen port. |
E1005 — DERP relay connection failed
Could not connect to the DERP relay server. The relay server may be down, the URL may be incorrect, or a firewall may be blocking WebSocket connections.
| Step | Resolution |
|---|---|
| 1 | Verify the DERP server URL in your config: nefia validate. |
| 2 | Check that the DERP server is running and reachable: curl -s https://<derp-host>/healthz. |
| 3 | Ensure outbound WebSocket (wss://) connections are not blocked by a firewall or proxy. |
| 4 | Run nefia vpn diagnose to test DERP connectivity. |
E1006 — DERP relay authentication failed
The DERP relay server rejected the authentication handshake. The WireGuard private key may be invalid, or the server's ACL may not include this client's public key.
| Step | Resolution |
|---|---|
| 1 | Verify your WireGuard private key is valid: nefia vpn diagnose. |
| 2 | If the DERP server uses an ACL, ensure your public key is in the allowed list. |
| 3 | Re-enroll the host if keys were rotated: nefia vpn reinvite --host <host>. |
| 4 | Contact the DERP server administrator if the issue persists. |
E1007 — Enrollment rate limited
Too many enrollment requests were sent from the same IP address. The operator rate-limits enrollment to prevent abuse.
| Step | Resolution |
|---|---|
| 1 | Wait at least 30 seconds before retrying enrollment. |
| 2 | If multiple agents are enrolling simultaneously, space out the requests. |
| 3 | Check for retry loops in automation scripts that may be flooding the enrollment endpoint. |
SSH Errors (E2xxx)
| Code | Title | Description |
|---|---|---|
| E2001 | SSH authentication failed | The SSH key or credentials were rejected by the remote host. |
| E2002 | SSH connection failed | Could not establish a TCP connection to the SSH server on the remote host. |
| E2003 | Command timed out | The remote command did not complete within the configured timeout. |
| E2004 | SSH session failed | An SSH session could not be opened on an existing connection. The connection may have been dropped. |
| E2005 | SSH host key changed | The SSH host key for the remote host has changed since the last connection. This may indicate a man-in-the-middle attack or that the host was reinstalled. |
E2001 — SSH authentication failed
The SSH key or credentials were rejected by the remote host.
| Step | Resolution |
|---|---|
| 1 | Verify the SSH key: ssh -i ~/.ssh/id_ed25519 <user>@<host>. |
| 2 | Check key file permissions: chmod 600 ~/.ssh/id_ed25519 (Linux/macOS) or use icacls to restrict access (Windows). |
| 3 | Ensure the public key is in the remote host's ~/.ssh/authorized_keys. |
| 4 | Re-enroll the host if SSH keys were rotated: nefia vpn reinvite --host <host>. |
E2002 — SSH connection failed
Could not establish a TCP connection to the SSH server on the remote host.
| Step | Resolution |
|---|---|
| 1 | Check that the VPN tunnel is active: nefia vpn diagnose --host <host>. |
| 2 | Verify the SSH daemon is running: systemctl status sshd (Linux) or Get-Service sshd (Windows). |
| 3 | Ensure the SSH port (22) is open and not blocked by a firewall. |
| 4 | Increase the connect timeout if the network is slow: ssh.connect_timeout_sec in config. |
E2003 — Command timed out
The remote command did not complete within the configured timeout.
| Step | Resolution |
|---|---|
| 1 | Increase the timeout: nefia exec --timeout 5m <command>. |
| 2 | Check if the command is blocked waiting for input (avoid interactive commands). |
| 3 | Verify the remote host is responsive: nefia vpn diagnose --host <host>. |
E2004 — SSH session failed
An SSH session could not be opened on an existing connection. The connection may have been dropped.
| Step | Resolution |
|---|---|
| 1 | Retry the command; the connection pool will reconnect automatically. |
| 2 | Check if the remote SSH server has a MaxSessions limit that is too low. |
| 3 | Reduce concurrency if many parallel sessions are failing: --concurrency <n>. |
E2005 — SSH host key changed
The SSH host key for the remote host has changed since the last connection. This may indicate a man-in-the-middle attack or that the host was reinstalled.
| Step | Resolution |
|---|---|
| 1 | If the host was reinstalled or its SSH keys were regenerated, remove the old entry from the known_hosts file. |
| 2 | Compare the old and new fingerprints shown in the error message to verify the change is expected. |
| 3 | Run nefia vpn diagnose --host <host> to check VPN and network integrity. |
| 4 | If unsure, contact the administrator of the remote host before proceeding. |
SFTP / Filesystem Errors (E3xxx)
| Code | Title | Description |
|---|---|---|
| E3001 | File not found | The specified file or directory does not exist on the remote host. |
| E3002 | Permission denied | The remote user does not have permission to access the requested file or directory. |
| E3003 | File too large | The file exceeds the maximum allowed size for transfer. |
| E3004 | SFTP operation failed | An SFTP file operation (read, write, list, or stat) failed on the remote host. |
| E3005 | Path denied by policy | The requested file path is outside the allowed roots or matches a deny rule in the policy configuration. |
E3001 — File not found
The specified file or directory does not exist on the remote host.
| Step | Resolution |
|---|---|
| 1 | Verify the file path is correct (paths are relative to the session root). |
| 2 | List files in the directory: nefia fs list --host <host> <parent-dir>. |
| 3 | Check that the path uses the correct separator for the remote OS. |
E3002 — Permission denied
The remote user does not have permission to access the requested file or directory.
| Step | Resolution |
|---|---|
| 1 | Check file ownership and permissions on the remote host. |
| 2 | Ensure the SSH user has read/write access to the target path. |
| 3 | Consider using a session with a different root directory. |
E3003 — File too large
The file exceeds the maximum allowed size for transfer.
| Step | Resolution |
|---|---|
| 1 | Use nefia fs read --out <local-path> to stream large files to disk (up to 3 GB). |
| 2 | Adjust ssh.max_file_size_bytes in the configuration if appropriate. |
| 3 | Consider compressing the file on the remote host before transfer. |
E3004 — SFTP operation failed
An SFTP file operation (read, write, list, or stat) failed on the remote host.
| Step | Resolution |
|---|---|
| 1 | Retry the operation; transient SFTP errors may resolve on reconnection. |
| 2 | Check that the SFTP subsystem is enabled on the remote SSH server. |
| 3 | Verify disk space on the remote host if writing files. |
| 4 | Review the full error with --verbose for details. |
E3005 — Path denied by policy
The requested file path is outside the allowed roots or matches a deny rule in the policy configuration.
| Step | Resolution |
|---|---|
| 1 | Check the allowed_roots and deny_paths in your policy configuration. |
| 2 | Use nefia validate to review the active policy rules. |
| 3 | Open a session with the correct root: nefia session open --host <host> --root <path>. |
Configuration Errors (E4xxx)
| Code | Title | Description |
|---|---|---|
| E4001 | Invalid configuration | The Nefia configuration file contains invalid or missing values. |
| E4002 | Invalid selector | The target selector expression could not be parsed. |
| E4003 | Host not found | The specified host ID was not found in the inventory. |
E4001 — Invalid configuration
The Nefia configuration file contains invalid or missing values.
| Step | Resolution |
|---|---|
| 1 | Run nefia validate to see detailed validation errors. |
| 2 | Re-initialize the config: nefia setup (this creates a new default config). |
| 3 | Check YAML syntax for indentation errors or unquoted special characters. |
E4002 — Invalid selector
The target selector expression could not be parsed.
| Step | Resolution |
|---|---|
| 1 | Use a valid selector format: host:<id>, group:<name>, tag:key=value, or all. |
| 2 | Run nefia hosts list to see available host IDs. |
| 3 | Run nefia groups list to see available group names. |
E4003 — Host not found
The specified host ID was not found in the inventory.
| Step | Resolution |
|---|---|
| 1 | Run nefia hosts list to see available hosts. |
| 2 | Check that the host ID is spelled correctly. |
| 3 | If the host was recently added, reload the configuration. |
Policy Errors (E5xxx)
| Code | Title | Description |
|---|---|---|
| E5001 | Command denied by policy | The command was blocked by the policy engine's deny rules. |
| E5002 | License limit exceeded | The operation requires a higher license tier or exceeds the current plan's limits. |
E5001 — Command denied by policy
The command was blocked by the policy engine's deny rules.
| Step | Resolution |
|---|---|
| 1 | Review the deny_commands list in your policy configuration. |
| 2 | If the command should be allowed, add it to allow_commands or adjust the policy. |
| 3 | Use nefia validate to inspect active policy rules. |
E5002 — License limit exceeded
The operation requires a higher license tier or exceeds the current plan's limits.
| Step | Resolution |
|---|---|
| 1 | Check your current plan: nefia account. |
| 2 | Upgrade your plan at nefia.ai/#pricing. |
| 3 | Remove unused hosts to free up capacity: nefia hosts remove <id>. |
Authentication Errors (E6xxx)
| Code | Title | Description |
|---|---|---|
| E6001 | Authentication required | You are not logged in. Most Nefia commands require authentication. |
| E6002 | Authentication token expired | Your login session has expired and needs to be refreshed. |
| E6003 | Device limit exceeded | You have reached the maximum number of registered devices for your plan. |
E6001 — Authentication required
You are not logged in. Most Nefia commands require authentication.
| Step | Resolution |
|---|---|
| 1 | Log in: nefia login. |
| 2 | Create an account at nefia.ai/sign-up if you don't have one. |
| 3 | Check if your session token file exists and is readable. |
E6002 — Authentication token expired
Your login session has expired and needs to be refreshed.
| Step | Resolution |
|---|---|
| 1 | Log in again: nefia login. |
| 2 | If this happens frequently, check your system clock is accurate. |
E6003 — Device limit exceeded
You have reached the maximum number of registered devices for your plan.
| Step | Resolution |
|---|---|
| 1 | Remove an unused host: nefia hosts remove <id>. |
| 2 | Check your current usage: nefia account. |
| 3 | Upgrade your plan for more device slots: nefia.ai/#pricing. |
Execution Errors (E7xxx)
| Code | Title | Description |
|---|---|---|
| E7001 | Command exited with non-zero status | The remote command completed but returned a non-zero exit code, indicating failure. |
| E7002 | Invalid playbook | The playbook definition contains syntax errors or invalid step configurations. |
E7001 — Command exited with non-zero status
The remote command completed but returned a non-zero exit code, indicating failure.
| Step | Resolution |
|---|---|
| 1 | Check the command's stderr output for error details. |
| 2 | Run the command with --verbose to see full output. |
| 3 | Verify the command syntax is correct for the remote host's shell. |
E7002 — Invalid playbook
The playbook definition contains syntax errors or invalid step configurations.
| Step | Resolution |
|---|---|
| 1 | Validate the playbook YAML for syntax errors. |
| 2 | Ensure each step has exactly one action: exec, fs_write, or fs_read. |
| 3 | Check that register names use only alphanumeric characters and underscores. |
| 4 | Verify when conditions reference valid registered step names. |
Team Errors (E8xxx)
| Code | Title | Description |
|---|---|---|
| E8001 | Team not found | The specified team does not exist or you do not have access to it. |
| E8002 | Insufficient team permissions | You do not have the required role to perform this action within the team. |
| E8003 | Team member limit exceeded | The team has reached the maximum number of members allowed by its plan. |
| E8004 | Invalid invitation code | The team invitation code is invalid, expired, or has reached its maximum usage count. |
| E8101 | Device lock denied | The device failed device-lock verification. The device's Ed25519 key does not match the registered key for this host. |
| E8102 | Posture check denied | The device did not meet the required posture policy (e.g. OS version, disk encryption, antivirus). |
| E8201 | Secret resolution failed | A secret reference could not be resolved. The secret provider may be unavailable or the secret may not exist. |
| E8202 | Cluster node is not the leader | This operation must be performed on the Raft cluster leader node. The current node is a follower. |
E8001 — Team not found
The specified team does not exist or you do not have access to it.
| Step | Resolution |
|---|---|
| 1 | List your teams: nefia team list. |
| 2 | Check the team slug or ID for typos. |
| 3 | Ask the team owner to send you an invitation if you haven't joined yet. |
E8002 — Insufficient team permissions
You do not have the required role to perform this action within the team.
| Step | Resolution |
|---|---|
| 1 | Check your role in the team: nefia team current. |
| 2 | Ask a team OWNER or ADMIN to grant you the necessary permissions. |
| 3 | Some actions (e.g. removing members, creating invitations) require ADMIN or OWNER role. |
E8003 — Team member limit exceeded
The team has reached the maximum number of members allowed by its plan.
| Step | Resolution |
|---|---|
| 1 | Remove inactive members: nefia team members, then contact an admin to remove unused accounts. |
| 2 | Upgrade the team plan for more member slots: nefia.ai/#pricing. |
E8004 — Invalid invitation code
The team invitation code is invalid, expired, or has reached its maximum usage count.
| Step | Resolution |
|---|---|
| 1 | Ask the team admin to generate a new invitation: nefia team invite. |
| 2 | Check that the invitation code was copied correctly (no extra spaces). |
| 3 | Invitation codes expire after the configured duration (default: 48 hours). |
E8101 — Device lock denied
The device failed device-lock verification. The device's Ed25519 key does not match the registered key for this host.
| Step | Resolution |
|---|---|
| 1 | Re-register the device: nefia device-lock register --host <host>. |
| 2 | Check that the device has not been re-imaged or cloned since registration. |
| 3 | Use nefia device-lock status to inspect the current device key. |
E8102 — Posture check denied
The device did not meet the required posture policy (e.g. OS version, disk encryption, antivirus).
| Step | Resolution |
|---|---|
| 1 | Run nefia posture check --host <host> to see which checks failed. |
| 2 | Update the device to meet the required posture rules defined in your policy. |
| 3 | Review posture rules in the config: nefia validate. |
E8201 — Secret resolution failed
A secret reference could not be resolved. The secret provider may be unavailable or the secret may not exist.
| Step | Resolution |
|---|---|
| 1 | Check that the secret exists in the configured provider: nefia secrets list. |
| 2 | Verify the secret provider is reachable (e.g. Vault server, AWS credentials). |
| 3 | Review the secret reference syntax in your config or playbook. |
E8202 — Cluster node is not the leader
This operation must be performed on the Raft cluster leader node. The current node is a follower.
| Step | Resolution |
|---|---|
| 1 | Run nefia cluster status to identify the current leader node. |
| 2 | Retry the command on the leader node, or wait for a leader election to complete. |
| 3 | If the cluster has no leader, check that a quorum of nodes is reachable. |
Internal Errors (E9xxx)
| Code | Title | Description |
|---|---|---|
| E9001 | Internal error | An unexpected internal error occurred. This is likely a bug. |
| E9002 | Operation cancelled | The operation was cancelled, either by the user (Ctrl+C) or a timeout. |
E9001 — Internal error
An unexpected internal error occurred. This is likely a bug.
| Step | Resolution |
|---|---|
| 1 | Retry the operation. |
| 2 | Run with --debug for detailed error information. |
| 3 | Report the issue at github.com/co-r-e/nefia-cli/issues with the debug output. |
E9002 — Operation cancelled
The operation was cancelled, either by the user (Ctrl+C) or a timeout.
| Step | Resolution |
|---|---|
| 1 | Retry the operation if it was interrupted unintentionally. |
| 2 | Increase the timeout if the operation needs more time: --timeout <duration>. |
Domain Code Mapping
The following table maps domain-specific error codes (used in JSON output and MCP responses) to their corresponding E-codes.
| Domain Code | E-Code | Category |
|---|---|---|
VPN_FAILED | E1001 | VPN |
VPN_SETUP_FAILED | E1004 | VPN |
DERP_CONNECT_FAILED | E1005 | VPN |
DERP_AUTH_FAILED | E1006 | VPN |
ENROLLMENT_RATE_LIMITED | E1007 | VPN |
SSH_AUTH_FAILED | E2001 | SSH |
SSH_CONNECT_FAILED | E2002 | SSH |
CMD_TIMEOUT | E2003 | SSH |
SSH_SESSION_FAILED | E2004 | SSH |
SSH_HOST_KEY_CHANGED | E2005 | SSH |
FILE_NOT_FOUND | E3001 | Filesystem |
PERMISSION_DENIED | E3002 | Filesystem |
FILE_TOO_LARGE | E3003 | Filesystem |
SFTP_FAILED | E3004 | Filesystem |
PATH_DENIED | E3005 | Filesystem |
CONFIG_INVALID | E4001 | Configuration |
SELECTOR_INVALID | E4002 | Configuration |
HOST_NOT_FOUND | E4003 | Configuration |
POLICY_DENIED | E5001 | Policy |
LICENSE_DENIED | E5002 | Policy |
AUTH_REQUIRED | E6001 | Authentication |
AUTH_EXPIRED | E6002 | Authentication |
DEVICE_LIMIT_EXCEEDED | E6003 | Authentication |
CMD_EXIT_NONZERO | E7001 | Execution |
PLAYBOOK_INVALID | E7002 | Execution |
TEAM_NOT_FOUND | E8001 | Team |
TEAM_ACCESS_DENIED | E8002 | Team |
TEAM_MEMBER_LIMIT | E8003 | Team |
TEAM_INVITE_INVALID | E8004 | Team |
DEVICE_LOCK_DENIED | E8101 | Security |
POSTURE_DENIED | E8102 | Security |
SECRET_RESOLVE_FAILED | E8201 | Secrets |
CLUSTER_NOT_LEADER | E8202 | Cluster |
SESSION_NOT_FOUND | E9001 | Internal |
INTERNAL | E9001 | Internal |
CANCELLED | E9002 | Internal |
SUDO_DISABLED | — | MCP |
SUDO_DENIED | E5001 | Policy |
JIT_REQUIRED | — | MCP |
VPN_NOT_READY | E1001 | VPN |
SCHEDULE_NOT_FOUND | — | MCP |
QUEUE_ENTRY_NOT_FOUND | — | MCP |
QUEUE_INVALID_STATE | — | MCP |
JOB_NOT_FOUND | — | MCP |
CONCURRENCY_LIMIT | — | MCP |
RATE_LIMITED | — | MCP |
AUTH_REQUIRED | — | MCP |
SERVICE_DEPLOY_FAILED | — | MCP |
CONFIG_APPLY_FAILED | — | MCP |
BASELINE_DRIFT | — | MCP |
MOUNT_FAILED | — | MCP |
CONTAINER_FAILED | — | MCP |
CLUSTER_JOIN_FAILED | — | MCP |
SSHCA_SIGNING_FAILED | — | MCP |
STREAM_TIMEOUT | — | MCP |
FEATURE_DISABLED | — | MCP |
METHOD_NOT_FOUND | — | MCP |
MCP-Specific Error Codes
The MCP server uses additional JSON-RPC error codes that are specific to the MCP protocol. These codes appear in the error.code field of JSON-RPC error responses, not in the Exxxx format.
| RPC Code | Domain Code | Description | Transient |
|---|---|---|---|
-32001 | POLICY_DENIED | Operation blocked by policy engine | No |
-32002 | PATH_DENIED | Path outside session root (sandbox escape) | No |
-32003 | SSH_AUTH_FAILED | SSH authentication failed | No |
-32004 | SSH_CONNECT_FAILED | SSH connection failed | Yes |
-32005 | CMD_TIMEOUT | Command exceeded timeout | Yes |
-32006 | SFTP_FAILED | SFTP operation failed (also covers FILE_NOT_FOUND, PERMISSION_DENIED, FILE_TOO_LARGE) | Conditional |
-32007 | SESSION_NOT_FOUND | Session ID does not exist or expired | No |
-32008 | HOST_NOT_FOUND | Host ID not found in configuration | No |
-32009 | VPN_FAILED | VPN tunnel operation failed | Yes |
-32010 | SESSION_REQUIRED | Session required but not provided | No |
-32011 | PLAYBOOK_FAILED | Playbook definition invalid or execution failed | No |
-32012 | COMMAND_FAILED | Command returned non-zero exit code | No |
-32013 | ACCESS_DENIED | Authentication, licensing, or permission error | No |
-32014 | CANCELLED | Operation cancelled or timed out | No |
-32015 | DEVICE_LOCK_DENIED | Device-lock verification failed | No |
-32016 | POSTURE_DENIED | Device posture check failed | No |
-32017 | SECRET_RESOLVE_FAILED | Secret reference could not be resolved | No |
-32018 | CLUSTER_NOT_LEADER | Operation must run on Raft cluster leader | No |
-32019 | SERVER_NOT_INITIALIZED | MCP client has not sent initialize handshake | No |
-32020 | SUDO_DISABLED | Sudo not enabled in configuration | No |
-32021 | SUDO_DENIED | Sudo command denied by policy | No |
-32022 | JIT_REQUIRED | Just-In-Time access required | No |
-32023 | VPN_NOT_READY | VPN tunnel not yet established | Yes |
-32024 | SCHEDULE_NOT_FOUND | Schedule not found | No |
-32025 | QUEUE_ENTRY_NOT_FOUND | Queue entry not found | No |
-32026 | QUEUE_INVALID_STATE | Queue entry in invalid state | No |
-32027 | JOB_NOT_FOUND | Job ID not found | No |
-32028 | CONCURRENCY_LIMIT | All dispatch slots in use | Yes |
-32029 | RATE_LIMITED | Rate limit exceeded | Yes |
-32030 | AUTH_REQUIRED | Authentication required for team/auth operation | No |
-32031 | SERVICE_DEPLOY_FAILED | Service deploy composite operation failed | No |
-32032 | CONFIG_APPLY_FAILED | Config apply composite operation failed | No |
-32033 | BASELINE_DRIFT | System baseline comparison detected drift | No |
-32034 | MOUNT_FAILED | Mount or unmount operation failed | No |
-32035 | CONTAINER_FAILED | Container operation failed | No |
-32036 | CLUSTER_JOIN_FAILED | Failed to add peer to Raft cluster | Yes |
-32037 | SSHCA_SIGNING_FAILED | SSH CA certificate signing failed | No |
-32038 | STREAM_TIMEOUT | File streaming operation timed out | Yes |
-32039 | FEATURE_DISABLED | Feature not enabled in nefia.yaml | No |
-32601 | METHOD_NOT_FOUND | Tool or method not found | No |
Transient errors include a retry_after_ms field in the response. AI agents should wait for the specified duration before retrying. Non-transient errors require a different approach (e.g., fixing configuration, using a different host, or requesting JIT access).
Each MCP error response also includes a recovery_tool field that suggests which MCP tool to call for resolution:
| Error Code | Recovery Tool | Description |
|---|---|---|
HOST_NOT_FOUND | nefia.hosts.list | List available hosts to find the correct ID |
SESSION_NOT_FOUND | nefia.session.open | Open a new session |
SESSION_REQUIRED | nefia.session.open | Open a session first |
POLICY_DENIED | nefia.policy.test | Pre-check policy before retrying |
SSH_AUTH_FAILED | nefia.vpn.diagnose | Diagnose connectivity |
SSH_CONNECT_FAILED | nefia.vpn.diagnose | Diagnose connectivity |
VPN_FAILED | nefia.vpn.diagnose | Diagnose VPN issues |
VPN_NOT_READY | nefia.vpn.diagnose | Wait and diagnose |
SFTP_FAILED | nefia.vpn.diagnose | Check connectivity |
POSTURE_DENIED | nefia.posture.check | Check posture compliance |
JIT_REQUIRED | nefia.jit.request | Request temporary access |
SCHEDULE_NOT_FOUND | nefia.schedule.list | List available schedules |
QUEUE_ENTRY_NOT_FOUND | nefia.queue.list | List queue entries |
PATH_DENIED | nefia.policy.test | Test path against policy |
CMD_TIMEOUT | nefia.sys.processes | Check running processes |
PLAYBOOK_FAILED | nefia.playbook.validate | Validate playbook definition |
COMMAND_FAILED | nefia.sys.logs | Check system logs |
ACCESS_DENIED | nefia.jit.request | Request elevated access |
DEVICE_LOCK_DENIED | nefia.device_lock.status | Check device lock status |
SECRET_RESOLVE_FAILED | nefia.secrets.test | Test secret provider |
CLUSTER_NOT_LEADER | nefia.status | Check cluster status |
QUEUE_INVALID_STATE | nefia.queue.list | List queue entries |
JOB_NOT_FOUND | nefia.status | Check system status |
CANCELLED | nefia.status | Check system status |
INVALID_PARAMS | nefia.tools.schema | Get tool parameter schema |
RATE_LIMITED | nefia.status | Check system status |
SERVER_NOT_INITIALIZED | nefia.status | Check server status |
CONCURRENCY_LIMIT | nefia.status | Check system status |
SERVICE_DEPLOY_FAILED | nefia.service.deploy | Retry service deployment |
CONFIG_APPLY_FAILED | nefia.config.apply | Retry config application |
BASELINE_DRIFT | nefia.system.baseline | Re-run baseline comparison |
MOUNT_FAILED | nefia.sys.mount.list | Check mount status |
CONTAINER_FAILED | nefia.container.list | Check container status |
CLUSTER_JOIN_FAILED | nefia.cluster.status | Check cluster status |
SSHCA_SIGNING_FAILED | nefia.sshca.status | Check SSH CA status |
STREAM_TIMEOUT | nefia.fs.stat | Check file status |
FEATURE_DISABLED | nefia.config.show | Check which features are enabled |
METHOD_NOT_FOUND | nefia.tools.schema | Get available tool schema |
Error System Architecture
Nefia's error system is composed of two layers: type-safe sentinel errors and structured error codes.
Sentinel Errors and errors.Is()
All domain errors are defined as sentinel error variables in the pkg/types package. By using the types.Errorf helper to wrap a sentinel with %w, matching via errors.Is() works correctly.
// Sentinel error definition (pkg/types/types.go)
var ErrVPNFailed = errors.New("VPN_FAILED")
// Creating an error — wrap the sentinel using types.Errorf
err := types.Errorf(types.ErrVPNFailed, "tunnel setup: %w", innerErr)
// Checking an error — match against the sentinel with errors.Is()
if errors.Is(err, types.ErrVPNFailed) {
// Handle as a VPN_FAILED category error
}Internally, types.Errorf is implemented as fmt.Errorf("%w: "+format, sentinel, args...), making it fully compatible with the standard Go errors.Is() / errors.As() unwrap chain. Multiple sentinels can also be chained together.
FormatStructured — User-Facing Error Display
errcodes.FormatStructured(err, verbose) matches an error against the registered error code entries and produces a structured, user-facing message.
- Verbose mode (
--verbose): Displays the error code, title, detailed description, all resolution steps, and a documentation link. - Brief mode (default): Displays only the error code, title, and the first resolution step.
Matching is performed in two stages:
- Fast path — Attempts to match against sentinel errors using
errors.Is()(O(1) lookup). - Fallback — Scans the error message string for a domain code (e.g.,
"VPN_FAILED") for backward compatibility.
SentinelFor — Reverse Lookup from Domain Code to Sentinel
errcodes.SentinelFor(code) retrieves the corresponding sentinel error variable from a domain code string (e.g., "SSH_AUTH_FAILED") in O(1) time. This is used when the MCP server or external tools receive a string-based error code and need to convert it into a sentinel that can be compared using errors.Is().
sentinel, ok := errcodes.SentinelFor("SSH_AUTH_FAILED")
if ok && errors.Is(err, sentinel) {
// Handle as an SSH authentication error
}MCP Error Classification
The MCP server (internal/mcp) uses only errors.Is() to classify domain errors into JSON-RPC error codes. It does not use the string-matching fallback.
// internal/mcp/mcp.go — domainErrorToRPCCode()
func domainErrorToRPCCode(err error) int {
switch {
case errors.Is(err, types.ErrPolicyDenied):
return codePolicyDenied
case errors.Is(err, types.ErrSSHAuthFailed):
return codeSSHAuthFailed
// ...
}
}Related
Numeric exit codes returned by the CLI and their meanings.
Complete reference for all Nefia CLI commands, flags, and options.