Skip to content

Error Handling ​

Learn how to properly handle errors in your Cognipeer Console SDK applications.

Error Types ​

The SDK throws two error classes. CognipeerAPIError extends CognipeerError, which extends the native Error:

typescript
class CognipeerError extends Error {
  statusCode?: number;   // HTTP status code
  response?: unknown;    // the parsed response body
}

class CognipeerAPIError extends CognipeerError {
  errorType?: string;    // the body's error.type, e.g. 'invalid_request_error'
  errorCode?: string;    // the body's error.code (or a top-level `code`) — branch on this
  retryAfterMs?: number; // parsed Retry-After header, in milliseconds, when present
}

An HTTP error response is always a CognipeerAPIError. A request that is aborted (your own signal, or the client timeout) rejects with the runtime's AbortError, not a CognipeerError.

Basic Error Handling ​

Always wrap SDK calls in try-catch blocks:

typescript
import { ConsoleClient, CognipeerError } from '@cognipeer/console-sdk';

const client = new ConsoleClient({ apiKey: 'your-api-key' });

try {
  const response = await client.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: 'Hello!' }],
  });
  
  console.log(response.choices[0].message.content);
} catch (error) {
  if (error instanceof CognipeerError) {
    console.error('Cognipeer Console SDK Error:', error.message);
    console.error('Status:', error.statusCode);
    console.error('Code:', error.errorCode);
  } else {
    console.error('Unexpected error:', error);
  }
}

Common Error Codes ​

Authentication Errors (401) ​

typescript
try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof CognipeerError && error.statusCode === 401) {
    console.error('Invalid API key. Please check your credentials.');
    // Handle authentication error
  }
}

Rate Limiting (429) ​

typescript
try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof CognipeerError && error.statusCode === 429) {
    console.error('Rate limit exceeded. Please retry after some time.');
    // Implement exponential backoff
  }
}

Invalid Request (400) ​

typescript
try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof CognipeerError && error.statusCode === 400) {
    console.error('Invalid request:', error.message);
    // Check request parameters
  }
}

Model Not Found (404) ​

typescript
try {
  const response = await client.chat.completions.create({
    model: 'non-existent-model',
    messages: [{ role: 'user', content: 'Hello' }],
  });
} catch (error) {
  if (error instanceof CognipeerError && error.statusCode === 404) {
    console.error('Model not found:', error.message);
    // Use a different model
  }
}

Server Errors (500+) ​

typescript
try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof CognipeerError && error.statusCode && error.statusCode >= 500) {
    console.error('Server error. Please try again later.');
    // Implement retry logic
  }
}

Machine-readable error codes ​

error.statusCode tells you the class of failure; error.errorCode tells you which rule was hit. Branch on it, never on the message text. The helpers isAgentRunErrorCode and isAgentActionErrorCode narrow the error and give you typed constants:

typescript
import {
  CognipeerAPIError,
  AgentRunErrorCodes,
  AgentActionErrorCodes,
  isAgentRunErrorCode,
  isAgentActionErrorCode,
} from '@cognipeer/console-sdk';

try {
  await client.agents.responses.create({ model: 'support-bot', session_id: id, input: 'Hi' });
} catch (error) {
  if (isAgentRunErrorCode(error, AgentRunErrorCodes.Conflict)) {
    // 409 agent_run_conflict: a run is already active on this conversation
  } else if (isAgentActionErrorCode(error, AgentActionErrorCodes.SessionNotFound)) {
    // 404 session_not_found
  } else if (isAgentActionErrorCode(error, AgentActionErrorCodes.PendingAction)) {
    // 409 pending_action: answer the open approvals/questions first
  } else if (error instanceof CognipeerAPIError && error.errorCode === 'agent_not_active') {
    // codes without a constant are plain strings
  } else {
    throw error;
  }
}

Webhook routes answer { error: '<message>', code: '<code>' }; the SDK reads the top-level code into errorCode too, so not_found, forbidden, limit_reached, endpoint_disabled and delivery_pending are available. Extra fields of the body (action_status, pending_action_ids, spent_usd, decisionId, …) are on error.response.

The full list of codes, with status and meaning, is on Agent error codes.

What the SDK retries for you ​

The client retries 429, 502, 503 and 504 up to maxRetries times (default 3) with exponential backoff, honouring Retry-After up to 30 seconds. A longer Retry-After is thrown with retryAfterMs set so you can reschedule. A server can veto the retry with { error: { retryable: false } } — a synchronous agent 504 timeout is never retried. Other statuses (400, 401, 402, 403, 404, 409, 410, …) are thrown at once.

Retry Logic ​

Implement exponential backoff for transient errors:

typescript
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  delay = 1000
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof CognipeerError) {
        // Retry on rate limits and server errors
        if (error.statusCode === 429 || (error.statusCode && error.statusCode >= 500)) {
          if (i < maxRetries - 1) {
            const waitTime = delay * Math.pow(2, i);
            console.log(`Retrying in ${waitTime}ms...`);
            await new Promise(resolve => setTimeout(resolve, waitTime));
            continue;
          }
        }
      }
      throw error;
    }
  }
  throw new Error('Max retries reached');
}

// Usage
try {
  const response = await withRetry(() =>
    client.chat.completions.create({
      model: 'gpt-4',
      messages: [{ role: 'user', content: 'Hello!' }],
    })
  );
  console.log(response);
} catch (error) {
  console.error('Failed after retries:', error);
}

Timeout Handling ​

Set custom timeouts for requests. When the timeout elapses, the request is aborted and rejects with an AbortError (the same error you get when you abort with your own signal):

typescript
const client = new ConsoleClient({
  apiKey: 'your-api-key',
  timeout: 30000, // 30 seconds
});

try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof Error && error.name === 'AbortError') {
    console.error('Request timed out or was aborted');
    // Handle timeout
  }
}

A synchronous agent turn that outlives the server's ceiling is different: the Console answers 504 with errorCode === 'timeout', and the SDK does not retry it (tool side effects may already have happened). Use background: true for long turns — see Agents → Background runs.

Validation Errors ​

Validate input before making API calls:

typescript
function validateMessages(messages: ChatMessage[]) {
  if (!messages || messages.length === 0) {
    throw new Error('Messages array cannot be empty');
  }
  
  for (const message of messages) {
    if (!message.role || !message.content) {
      throw new Error('Each message must have role and content');
    }
  }
}

try {
  validateMessages(messages);
  const response = await client.chat.completions.create({
    model: 'gpt-4',
    messages,
  });
} catch (error) {
  console.error('Validation error:', error);
}

Streaming Errors ​

Handle errors in streaming responses:

typescript
try {
  const stream = await client.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: 'Hello!' }],
    stream: true,
  });

  for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content || '');
  }
} catch (error) {
  if (error instanceof CognipeerError) {
    console.error('Streaming error:', error.message);
  }
}

Error Recovery ​

Implement graceful degradation:

typescript
async function getChatResponse(prompt: string) {
  const models = ['gpt-4', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k'];
  
  for (const model of models) {
    try {
      const response = await client.chat.completions.create({
        model,
        messages: [{ role: 'user', content: prompt }],
      });
      
      return response.choices[0].message.content;
    } catch (error) {
      if (error instanceof CognipeerError) {
        if (error.statusCode === 404) {
          console.log(`Model ${model} not available, trying next...`);
          continue;
        }
      }
      throw error;
    }
  }
  
  throw new Error('All models failed');
}

Best Practices ​

  1. Always catch errors: Never leave SDK calls unhandled
  2. Check error types: Use instanceof CognipeerError to identify SDK errors
  3. Log errors: Include status codes and error messages in logs
  4. Implement retries: Use exponential backoff for transient errors
  5. Validate input: Check parameters before making API calls
  6. Set timeouts: Configure appropriate timeouts for your use case
  7. Graceful degradation: Have fallback strategies for failures
  8. Monitor errors: Track error rates in production

Error Response Format ​

The API returns errors in the following format:

json
{
  "error": {
    "message": "Invalid API key",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}

Network Errors ​

Handle network-related issues:

typescript
try {
  const response = await client.chat.completions.create({...});
} catch (error) {
  if (error instanceof TypeError && error.message.includes('fetch')) {
    console.error('Network error. Check your internet connection.');
  } else if (error instanceof CognipeerError) {
    console.error('API error:', error.message);
  }
}

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub