Skip to main content
Error codes, recovery strategies, and best practices for ABP error handling.

Error Response Format

All ABP errors follow this structure:
interface ABPError {
  code: string;              // Machine-readable error code
  message: string;           // Human-readable message
  details?: unknown;         // Additional context
  retryable: boolean;        // Can this operation be retried?
  retryAfter?: number;       // Suggested retry delay in ms
  alternatives?: string[];   // Alternative capabilities to try
}

Standard Error Codes

Session Errors

CodeDescriptionRetryable
NOT_INITIALIZEDSession not initialized, call initialize() firstYes
ALREADY_INITIALIZEDSession already initializedNo
SESSION_EXPIREDSession has expiredYes (reinitialize)
SESSION_INVALIDSession ID is not validYes (reinitialize)

Capability Errors

CodeDescriptionRetryable
UNKNOWN_CAPABILITYCapability doesn’t existNo
CAPABILITY_UNAVAILABLECapability exists but isn’t available nowDepends
REQUIREMENT_NOT_METCapability requirement not satisfiedDepends
PRECONDITION_FAILEDPrecondition for capability not metDepends

Parameter Errors

CodeDescriptionRetryable
INVALID_PARAMSParameters don’t match schemaNo
MISSING_PARAMRequired parameter missingNo
INVALID_TYPEParameter type mismatchNo

Permission Errors

CodeDescriptionRetryable
PERMISSION_DENIEDUser denied permissionYes (after user grants)
PERMISSION_REQUIREDPermission required but not requestedYes

Execution Errors

CodeDescriptionRetryable
OPERATION_FAILEDOperation failedDepends on cause
TIMEOUTOperation timed outYes
CANCELLEDOperation was cancelledNo
RATE_LIMITEDToo many requestsYes (with backoff)
NOT_IMPLEMENTEDCapability declared but not implementedNo

System Errors

CodeDescriptionRetryable
BROWSER_ERRORBrowser API errorDepends
NOT_SUPPORTEDFeature not supportedNo
INTERNAL_ERRORUnexpected internal errorMaybe

Transport Errors

CodeDescriptionRetryable
TRANSPORT_ERRORTransport layer errorDepends
DISCONNECTEDConnection to app lostYes (reconnect)

MCP Bridge Error Handling

When using the ABP MCP Bridge, errors are surfaced as structured MCP responses with isError: true. The bridge adds context-specific error information beyond what the ABP protocol itself defines.

Connection Errors

ErrorCauseWhat to do
url: Invalid urlURL doesn’t pass validationProvide a full URL with protocol (e.g., http://localhost:4765)
Failed to fetch app page: HTTP 404App URL is wrong or app isn’t runningCheck the URL and ensure the app is running
No <link rel="abp-manifest"> found in page headPage exists but doesn’t have an ABP manifest linkThe app may not implement ABP
Manifest missing required field: abpManifest JSON is malformedCheck the app’s manifest file
window.abp not found after page loadPage loaded but window.abp was never definedThe app may not implement ABP, or it takes too long to initialize
Browser launch timeoutPuppeteer couldn’t start Chromium in timeIncrease ABP_BROWSER_TIMEOUT or check if Chrome is installed

Capability Call Errors

ABP apps return structured errors with error codes. The bridge forwards these with retryable flags:
Error codeMeaningRetryable
INVALID_PARAMSParameters didn’t match the capability’s input schemaNo — check abp_status for required params
UNKNOWN_CAPABILITYCapability name doesn’t existNo — check abp_status for available capabilities
CAPABILITY_UNAVAILABLECapability exists but can’t be used right nowMaybe — check requirements
PERMISSION_DENIEDBrowser permission was deniedYes — may need user interaction in the browser
NOT_INITIALIZEDSession expired or wasn’t initializedYes — reconnect with abp_connect
OPERATION_FAILEDThe operation failed during executionMaybe
TIMEOUTOperation exceeded the timeoutYes — increase ABP_CALL_TIMEOUT
NOT_IMPLEMENTEDCapability is declared but not implemented by the appNo
Error responses include the retryable flag: "TIMEOUT: Operation timed out (retryable)".

Browser Errors

The bridge detects browser context loss (detached frame, target closed, session closed) and surfaces clean error messages. If the browser disconnects unexpectedly, the bridge logs a warning and resets internal state. A new abp_connect call will launch a fresh browser. The BrowserEventGuard monitors for page crashes and page closures. If either occurs, the bridge status is set to error with a descriptive message, and subsequent abp_call attempts will return "Browser page is no longer available — reconnect with abp_connect" instead of cryptic “Target closed” errors. Dialogs (alert, confirm, prompt, beforeunload) are auto-handled by the guard so they never block capability execution. The guard logs every handled dialog for debugging (ABP_LOG_LEVEL=info). See Troubleshooting for step-by-step resolution of common errors.

Error Handling Patterns

Client-Side (Agent/Bridge)

async function callCapability(capability, params) {
  try {
    const result = await abp.call(capability, params);

    if (result.success) {
      return result.data;
    }

    // Handle error
    const error = result.error;

    if (error.retryable) {
      // Retry with exponential backoff
      const delay = error.retryAfter || 1000;
      await sleep(delay);
      return await callCapability(capability, params);
    }

    // Check for alternatives
    if (error.alternatives && error.alternatives.length > 0) {
      console.log(`Try alternative: ${error.alternatives[0]}`);
    }

    throw new Error(`Capability failed: ${error.message}`);
  } catch (err) {
    console.error('Failed to call capability:', err);
    throw err;
  }
}

App-Side (Implementation)

async convertMarkdownToHtml({ markdown, options }) {
  try {
    // Attempt operation
    const html = await renderMarkdown(markdown, options);

    return {
      success: true,
      data: { html }
    };
  } catch (error) {
    // Map browser errors to ABP error codes
    let code = 'OPERATION_FAILED';
    let retryable = false;

    if (error.name === 'NotAllowedError') {
      code = 'PERMISSION_DENIED';
      retryable = true;
    } else if (error.name === 'SecurityError') {
      code = 'PERMISSION_REQUIRED';
      retryable = true;
    }

    return {
      success: false,
      error: {
        code,
        message: error.message,
        retryable
      }
    };
  }
}

Graceful Degradation

When a capability fails, the error response may include an alternatives array suggesting fallback capabilities. Use this to degrade gracefully:
async function callWithFallback(capability, params) {
  const result = await abp.call(capability, params);

  if (result.success) {
    return result.data;
  }

  const error = result.error;

  // Try alternatives if available
  if (error.alternatives && error.alternatives.length > 0) {
    for (const alternative of error.alternatives) {
      console.log(`Primary capability failed, trying alternative: ${alternative}`);
      const fallback = await abp.call(alternative, params);

      if (fallback.success) {
        return fallback.data;
      }
    }
  }

  throw new Error(`Capability ${capability} failed with no viable alternatives: ${error.message}`);
}

Retry Strategies

Exponential Backoff

async function callWithRetry(capability, params, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const result = await abp.call(capability, params);

    if (result.success) {
      return result.data;
    }

    lastError = result.error;

    if (!lastError.retryable) {
      throw new Error(`Not retryable: ${lastError.message}`);
    }

    // Exponential backoff: 1s, 2s, 4s
    const delay = (lastError.retryAfter || 1000) * Math.pow(2, attempt);
    console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
    await sleep(delay);
  }

  throw new Error(`Max retries exceeded: ${lastError.message}`);
}

Best Practices

  1. Always check success flag: Don’t assume success
  2. Respect retryable flag: Don’t retry non-retryable errors
  3. Use exponential backoff: Prevent overwhelming the app
  4. Log errors: Include error codes for debugging
  5. Provide alternatives: Suggest fallback capabilities
  6. Handle gracefully: Don’t crash on capability errors

Next Steps