-
Notifications
You must be signed in to change notification settings - Fork 0
Error Handling
Every error MikroMCP returns includes a machine-readable category, code, and recoverability block:
| Category | When it appears |
|---|---|
VALIDATION |
Invalid input parameters (Zod schema rejection, or no router could be resolved — MISSING_ROUTER_ID) |
NOT_FOUND |
Resource does not exist on the router |
CONFLICT |
Resource exists but with different configuration |
PERMISSION_DENIED |
Identity lacks the required role or the call is outside a maintenance window |
APPROVAL_REQUIRED |
Destructive operation requires a two-step confirmation token |
ROUTER_UNREACHABLE |
Network or TLS connectivity failure to the router |
ROUTER_AUTH_FAILED |
Bad credentials or insufficient RouterOS API policy |
ROUTER_TIMEOUT |
Request timed out waiting for a RouterOS response |
ROUTER_ERROR |
RouterOS accepted the connection but rejected the operation |
ROUTER_BUSY |
Circuit breaker is open (CIRCUIT_OPEN) — router is being protected after repeated failures |
INTERNAL |
Unexpected server-side error; check server logs |
CONFIGURATION |
Missing or invalid server configuration (routers.yaml, env vars) |
{
"category": "CONFLICT",
"code": "ROUTE_CONFLICT",
"message": "Route 10.0.0.0/8 via 192.168.1.1 already exists with distance=5. Requested distance=1.",
"details": {
"existing": { "distance": "5", "disabled": "false" },
"requested": { "distance": "1", "disabled": "false" }
},
"recoverability": {
"retryable": false,
"suggestedAction": "Remove the existing route first, or use manage_route with action=remove before re-adding.",
"alternativeTools": ["manage_route with action=remove"]
}
}Retryable errors include a retryAfterMs hint. The AI assistant uses the suggestedAction and alternativeTools fields to decide what to try next without user input.
Read-only tools automatically retry on transient network errors (ECONNREFUSED, ECONNRESET, ETIMEDOUT) and HTTP 5xx responses.
Backoff formula: min(baseDelay × 2^attempt + jitter, maxDelay)
Defaults: 3 attempts, 200 ms base delay, 5 s cap.
Write tools skip the retry layer to prevent double-firing a partial write.
Per-router, three-state (closed → open → half-open):
| State | Behaviour |
|---|---|
| Closed (normal) | All requests pass through |
| Open (tripped) | All requests fail immediately with ROUTER_BUSY; no traffic sent to the router |
| Half-open (probing) | After 30 s cooldown, one probe request is allowed through |
The breaker opens after 5 consecutive failures. It closes on the first successful probe. VALIDATION, NOT_FOUND, and CONFLICT errors do not count as failures — only genuine connectivity or server errors trip it.
Each router has an independent circuit breaker — one unreachable router does not affect tools targeting other routers.
All write tools check existing state before acting and return a structured action field in the response:
action value |
Meaning |
|---|---|
created / added
|
New resource was created |
updated |
Existing resource was modified |
removed |
Resource was deleted |
already_exists |
Resource already matches requested config — no change made |
no_change |
Resource already in requested state — no change made |
already_removed |
Resource did not exist — no change made |
dry_run |
dryRun: true was set — change was previewed but not applied |
would_fail |
plan_changes predicted this step would fail |
Use dryRun: true on any write tool to preview the planned change without touching the router.