Skip to content
openevidence-common-errors logo

OpenEvidence Common Errors

openevidence-common-errors

Diagnose and resolve common OpenEvidence API errors. Use when encountering error codes, debugging failed requests, or implementing error handling for clinical queries. Trigger with phrases like "openevidence error", "openevidence failing", "fix openevidence", "openevidence debug", "openevidence 4...

SKILL.md

Full skill instructions

OpenEvidence Common Errors

Overview

Comprehensive guide to diagnosing and resolving OpenEvidence API errors with healthcare-specific considerations.

Prerequisites

  • OpenEvidence SDK installed
  • Access to application logs
  • Understanding of HTTP status codes

Error Code Reference

Authentication Errors (4xx)

CodeErrorCauseSolution
401Invalid API KeyKey missing, malformed, or revokedVerify OPENEVIDENCE_API_KEY is set correctly
401Organization Not FoundInvalid orgIdCheck organization ID in dashboard
401BAA Not SignedBusiness Associate Agreement requiredContact [email protected]
403ForbiddenInsufficient permissions or suspended accountCheck account status in dashboard
403IP Not WhitelistedRequest from non-approved IPAdd IP to allowlist in security settings

Request Errors (4xx)

CodeErrorCauseSolution
400Invalid QueryMalformed request bodyCheck request schema
400Query Too ShortQuestion too briefProvide more clinical context
400Invalid SpecialtyUnknown specialty codeUse valid specialty from enum
404Resource Not FoundInvalid consultId or endpointVerify ID and URL
422UnprocessableValid JSON but invalid clinical queryRephrase question
429Rate LimitedToo many requestsImplement backoff, check quotas

Server Errors (5xx)

CodeErrorCauseSolution
500Internal Server ErrorOpenEvidence backend issueRetry with backoff, contact support if persists
502Bad GatewayUpstream service issueWait and retry
503Service UnavailableMaintenance or overloadCheck status.openevidence.com
504Gateway TimeoutRequest took too longSimplify query, increase timeout

Error Handling Implementation

Step 1: Custom Error Classes

// src/​openevidence/​errors.ts
export class OpenEvidenceError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number,
    public readonly retryable: boolean,
    public readonly originalError?: Error
  ) {
    super(message);
    this.name = 'OpenEvidenceError';
  }

  static fromApiError(error: any): OpenEvidenceError {
    const statusCode = error.response?.status || 500;
    const message = error.response?.data?.message || error.message;
    const code = error.response?.data?.code || 'UNKNOWN_ERROR';

    return new OpenEvidenceError(
      message,
      code,
      statusCode,
      isRetryable(statusCode),
      error
    );
  }
}

export class AuthenticationError extends OpenEvidenceError {
  constructor(message: string, code: string) {
    super(message, code, 401, false);
    this.name = 'AuthenticationError';
  }
}

export class RateLimitError extends OpenEvidenceError {
  constructor(
    message: string,
    public readonly retryAfter: number,
    public readonly limit: number,
    public readonly remaining: number
  ) {
    super(message, 'RATE_LIMITED', 429, true);
    this.name = 'RateLimitError';
  }
}

export class QueryValidationError extends OpenEvidenceError {
  constructor(
    message: string,
    public readonly validationErrors: string[]
  ) {
    super(message, 'VALIDATION_ERROR', 400, false);
    this.name = 'QueryValidationError';
  }
}

function isRetryable(statusCode: number): boolean {
  return statusCode === 429 || statusCode >= 500;
}

Step 2: Error Handler Wrapper

// src/​openevidence/​error-handler.ts
import {
  OpenEvidenceError,
  AuthenticationError,
  RateLimitError,
  QueryValidationError,
} from './​errors';

export async function withErrorHandling<T>(
  operation: () => Promise<T>,
  context?: { operation?: string; queryId?: string }
): Promise<T> {
  try {
    return await operation();
  } catch (error: any) {
    const oeError = classifyError(error);

    // Log for debugging (without PHI)
    console.error(`[OpenEvidence Error]`, {
      code: oeError.code,
      statusCode: oeError.statusCode,
      operation: context?.operation,
      retryable: oeError.retryable,
    });

    throw oeError;
  }
}

function classifyError(error: any): OpenEvidenceError {
  const status = error.response?.status;
  const data = error.response?.data;

  switch (status) {
    case 401:
      return new AuthenticationError(
        data?.message || 'Authentication failed',
        data?.code || 'AUTH_FAILED'
      );

    case 429:
      return new RateLimitError(
        'Rate limit exceeded',
        parseInt(error.response?.headers?.['retry-after'] || '60'),
        parseInt(error.response?.headers?.['x-ratelimit-limit'] || '0'),
        parseInt(error.response?.headers?.['x-ratelimit-remaining'] || '0')
      );

    case 400:
    case 422:
      return new QueryValidationError(
        data?.message || 'Invalid query',
        data?.errors || []
      );

    default:
      return OpenEvidenceError.fromApiError(error);
  }
}

Step 3: Retry Logic with Exponential Backoff

// src/​openevidence/​retry.ts
import { OpenEvidenceError, RateLimitError } from './​errors';

interface RetryConfig {
  maxRetries: number;
  baseDelayMs: number;
  maxDelayMs: number;
  jitterMs: number;
}

const DEFAULT_CONFIG: RetryConfig = {
  maxRetries: 3,
  baseDelayMs: 1000,
  maxDelayMs: 30000,
  jitterMs: 500,
};

export async function withRetry<T>(
  operation: () => Promise<T>,
  config: Partial<RetryConfig> = {}
): Promise<T> {
  const cfg = { ...DEFAULT_CONFIG, ...config };

  for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      if (!(error instanceof OpenEvidenceError) || !error.retryable) {
        throw error;
      }

      if (attempt === cfg.maxRetries) {
        throw error;
      }

      // Use Retry-After header for rate limits
      let delay: number;
      if (error instanceof RateLimitError && error.retryAfter > 0) {
        delay = error.retryAfter * 1000;
      } else {
        delay = Math.min(
          cfg.baseDelayMs * Math.pow(2, attempt) + Math.random() * cfg.jitterMs,
          cfg.maxDelayMs
        );
      }

      console.log(`Retry ${attempt + 1}/​${cfg.maxRetries} after ${delay}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }

  throw new Error('Unreachable');
}

Step 4: User-Facing Error Messages

// src/​openevidence/​error-messages.ts
import { OpenEvidenceError, RateLimitError, QueryValidationError } from './​errors';

export function getUserFriendlyMessage(error: OpenEvidenceError): string {
  switch (error.code) {
    case 'AUTH_FAILED':
    case 'INVALID_API_KEY':
      return 'Unable to connect to medical evidence service. Please contact support.';

    case 'BAA_REQUIRED':
      return 'Service configuration required. Please contact your administrator.';

    case 'RATE_LIMITED':
      const rle = error as RateLimitError;
      return `Service temporarily unavailable. Please try again in ${rle.retryAfter} seconds.`;

    case 'VALIDATION_ERROR':
      const qve = error as QueryValidationError;
      return `Please rephrase your clinical question. ${qve.validationErrors.join('. ')}`;

    case 'QUERY_TOO_SHORT':
      return 'Please provide more details about your clinical question.';

    case 'INVALID_SPECIALTY':
      return 'Please select a valid medical specialty.';

    case 'SERVICE_UNAVAILABLE':
      return 'Medical evidence service is temporarily unavailable. Please try again later.';

    default:
      if (error.statusCode >= 500) {
        return 'Medical evidence service is experiencing issues. Please try again later.';
      }
      return 'Unable to process your request. Please try again or contact support.';
  }
}

Output

  • Classified errors with appropriate handling
  • Retry logic for transient failures
  • User-friendly error messages
  • Comprehensive error logging (without PHI)

Diagnostic Commands

Quick Health Check

# Check if OpenEvidence is reachable
curl -s -o /​dev/​null -w "%{http_code}" \
  -H "Authorization: Bearer ${OPENEVIDENCE_API_KEY}" \
  https://api.openevidence.com/health

# Expected: 200

Check Rate Limit Status

curl -s -D - \
  -H "Authorization: Bearer ${OPENEVIDENCE_API_KEY}" \
  https://api.openevidence.com/v1/rate-limit \
  | grep -i "x-ratelimit"

# Headers show current limits

Error Handling

Error PatternDetectionResolution
Intermittent 5xx> 5% error rateEnable circuit breaker
Persistent 401All requests failRotate API key
Spike in 429Rate limit headersImplement request queuing
Timeout errorsP99 > 30sCheck network, simplify queries

Examples

Complete Error-Handled Query

async function safeClinicalQuery(question: string) {
  try {
    return await withRetry(
      () => withErrorHandling(
        () => client.query({ question, context: { specialty: 'internal-medicine', urgency: 'routine' } }),
        { operation: 'clinical-query' }
      ),
      { maxRetries: 3 }
    );
  } catch (error) {
    if (error instanceof OpenEvidenceError) {
      return {
        success: false,
        error: getUserFriendlyMessage(error),
        code: error.code,
      };
    }
    throw error;
  }
}

Resources

Next Steps

For comprehensive debugging, see openevidence-debug-bundle.