Skip to content

Error Handling

github-actions[bot] edited this page May 30, 2026 · 3 revisions

Error Handling

Error Categories

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)

Error Response Shape

{
  "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.


Retry Engine

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.


Circuit Breaker

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.


Idempotency and Dry-Run

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.

Clone this wiki locally