Error Handling Reference
Last Updated: January 9, 2026 SDK Version: 0.29.0 Sources:
src/resilience/types.ts,src/*/validators.ts,src/graph/types.ts
Overview
Cortex uses structured errors with specific error codes for predictable error handling. Errors are categorized into:
- Validation Errors - Client-side validation (instant feedback)
- Resilience Errors - Overload protection (rate limits, circuit breaker)
- Graph Errors - Graph database operations
- Cascade Errors - Deletion cascade operations
- Convex Backend Errors - Backend operation failures
Import Error Classes:
import {
// Validation errors
MemoryValidationError,
ConversationValidationError,
FactsValidationError,
UserValidationError,
AgentValidationError,
// Resilience errors
CircuitOpenError,
RateLimitExceededError,
QueueFullError,
AcquireTimeoutError,
// Graph errors
GraphDatabaseError,
GraphConnectionError,
GraphQueryError,
// Other errors
A2ATimeoutError,
CascadeDeletionError,
} from "@cortexmemory/sdk";
Table of Contents
- Error Class Hierarchy
- Error Handling Patterns
- Validation Errors
- Resilience Errors
- Graph Errors
- Other Errors
- Troubleshooting Guide
Error Class Hierarchy
Error (native JavaScript)
├── ValidationError (client-side validation)
│ ├── MemoryValidationError
│ ├── ConversationValidationError
│ ├── FactsValidationError
│ ├── UserValidationError
│ ├── AgentValidationError
│ ├── SessionValidationError
│ ├── AuthValidationError
│ ├── ImmutableValidationError
│ ├── MutableValidationError
│ ├── GovernanceValidationError
│ ├── MemorySpaceValidationError
│ ├── ContextsValidationError
│ └── A2AValidationError
├── ResilienceError (resilience layer)
│ ├── RateLimitExceededError
│ ├── CircuitOpenError
│ ├── QueueFullError
│ └── AcquireTimeoutError
├── GraphDatabaseError (graph operations)
│ ├── GraphConnectionError
│ │ └── GraphAuthenticationError
│ ├── GraphQueryError
│ └── GraphNotFoundError
├── CascadeDeletionError
├── AgentCascadeDeletionError
├── A2ATimeoutError
└── ResumableStreamError
Error Handling Patterns
Basic Error Handling
import { MemoryValidationError, CircuitOpenError } from "@cortexmemory/sdk";
try {
await cortex.memory.remember({
memorySpaceId: "user-123-space",
conversationId: "conv-123",
userMessage: "My name is Alex",
agentResponse: "Nice to meet you, Alex!",
userId: "user-123",
userName: "Alex",
agentId: "assistant",
});
} catch (error) {
if (error instanceof MemoryValidationError) {
// Client-side validation error (instant)
console.error(`Validation: ${error.code}`);
console.error(`Field: ${error.field}`);
console.error(`Message: ${error.message}`);
} else if (error instanceof CircuitOpenError) {
// Backend is unhealthy
console.error("Service temporarily unavailable");
console.error(`Retry after: ${error.retryAfterMs}ms`);
} else {
// Unexpected error
throw error;
}
}
Validation Error Handling
import { MemoryValidationError } from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof MemoryValidationError) {
// Instant client-side feedback
switch (error.code) {
case 'MISSING_REQUIRED_FIELD':
console.error(`Required field missing: ${error.field}`);
break;
case 'INVALID_IMPORTANCE':
console.error(`Invalid importance value for ${error.field}`);
break;
case 'OWNER_REQUIRED':
console.error('Must provide userId or agentId');
break;
default:
console.error(`Validation error: ${error.code}`);
}
}
}
Resilience Error Handling
import {
CircuitOpenError,
QueueFullError,
RateLimitExceededError
} from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof CircuitOpenError) {
// Backend is unhealthy, retry later
const retryAfter = error.retryAfterMs || 30000;
console.log(`Circuit open, retry in ${retryAfter}ms`);
scheduleRetry(request, retryAfter);
} else if (error instanceof QueueFullError) {
// System overloaded
console.error(`Queue full for priority: ${error.priority}`);
return { status: 503, message: 'Service temporarily unavailable' };
} else if (error instanceof RateLimitExceededError) {
// Rate limit hit
console.log(`Rate limited, refill in ${error.refillInMs}ms`);
await new Promise(resolve => setTimeout(resolve, error.refillInMs));
// Retry request
}
}
Graph Error Handling
import {
GraphConnectionError,
GraphAuthenticationError,
GraphQueryError,
} from "@cortexmemory/sdk";
// Graph sync is automatic when CORTEX_GRAPH_SYNC=true (v0.29.0+)
try {
await cortex.memory.remember({
// ... params
// No syncToGraph option needed - sync is automatic
});
} catch (error) {
if (error instanceof GraphAuthenticationError) {
console.error("Graph authentication failed");
console.error(`URI: ${error.uri}`);
console.error(`Username: ${error.username}`);
console.error("Check NEO4J_PASSWORD in .env");
} else if (error instanceof GraphConnectionError) {
console.error("Cannot connect to graph database");
// App continues working - graph is optional
} else if (error instanceof GraphQueryError) {
console.error("Graph query failed");
console.error(`Query: ${error.query}`);
}
}
Validation Errors
Memory Validation Errors
Class: MemoryValidationError
Source: src/memory/validators.ts
Structure:
class MemoryValidationError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly field?: string,
);
}
Error Codes:
| Code | Field | Description | Fix |
|---|---|---|---|
MISSING_REQUIRED_FIELD | memorySpaceId | Missing or empty memorySpaceId | Provide valid memorySpaceId |
MISSING_REQUIRED_FIELD | conversationId | Missing or empty conversationId | Provide valid conversationId |
MISSING_REQUIRED_FIELD | userMessage | Missing or empty userMessage | Provide message content |
MISSING_REQUIRED_FIELD | agentResponse | Missing or empty agentResponse | Provide response content |
MISSING_REQUIRED_FIELD | userName | Required when userId provided | Provide userName with userId |
OWNER_REQUIRED | userId/agentId | Neither userId nor agentId provided | Provide at least one owner ID |
INVALID_IMPORTANCE | importance | Not between 0-100 | Use number 0-100 |
INVALID_ID_FORMAT | (any ID field) | Invalid characters in ID | Use alphanumeric, hyphens, underscores |
INVALID_FORMAT | contentType | Invalid contentType | Use "raw" or "summarized" |
INVALID_SOURCE_TYPE | sourceType | Invalid sourceType | Use "conversation", "system", "tool", or "a2a" |
INVALID_EMBEDDING | embedding | Invalid embedding array | Provide array of finite numbers |
INVALID_TIMESTAMP | (timestamp fields) | Invalid timestamp | Use valid number or Date |
EMPTY_ARRAY | messageIds | Empty array | Provide at least one element |
MISSING_CONVERSATION_REF | conversationRef | Required for conversation source | Provide conversationRef |
INVALID_STREAM | responseStream | Invalid stream object | Use ReadableStream or AsyncIterable |
INVALID_FILTER | (filter) | Insufficient filter criteria | Add userId or sourceType filter |
INVALID_CONFIDENCE | minConfidence | Not between 0-100 | Use number 0-100 |
INVALID_DATE_RANGE | createdAfter/createdBefore | Invalid date range | Ensure after < before |
INVALID_GRAPH_DEPTH | graphExpansion.maxDepth | Not between 1-5 | Use depth 1-5 |
NEGATIVE_NUMBER | limit, version | Negative or invalid number | Use positive integer |
Example:
import { MemoryValidationError } from "@cortexmemory/sdk";
try {
await cortex.memory.remember({
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi!",
userId: "user-123",
// Missing userName - will throw validation error
});
} catch (error) {
if (error instanceof MemoryValidationError) {
console.error(error.code); // "MISSING_REQUIRED_FIELD"
console.error(error.field); // "userName"
console.error(error.message); // "userName is required when userId is provided"
}
}
Conversation Validation Errors
Class: ConversationValidationError
Source: src/conversations/validators.ts
Error Codes:
| Code | Field | Description | Fix |
|---|---|---|---|
MISSING_REQUIRED_FIELD | (various) | Required field missing | Provide required field |
INVALID_TYPE | type | Invalid conversation type | Use "user-agent" or "agent-agent" |
INVALID_ROLE | role | Invalid message role | Use "user", "agent", or "system" |
INVALID_ID_FORMAT | conversationId, messageId | Invalid ID format | Remove invalid characters |
INVALID_FORMAT | format | Invalid export format | Use "json" or "csv" |
INVALID_SORT_ORDER | sortOrder | Invalid sort order | Use "asc" or "desc" |
EMPTY_STRING | query | Empty search query | Provide non-empty query |
INVALID_RANGE | limit, offset | Invalid range | Use valid positive integers |
EMPTY_ARRAY | (array fields) | Empty array | Provide at least one element |
INVALID_ARRAY_LENGTH | (array fields) | Array length constraint violated | Check min/max requirements |
INVALID_DATE_RANGE | (date fields) | Start >= End | Ensure start < end |
INVALID_PARTICIPANTS | participants | Invalid participants structure | Match type requirements |
DUPLICATE_VALUES | (array fields) | Duplicate values in array | Remove duplicates |
Facts Validation Errors
Class: FactsValidationError
Source: src/facts/validators.ts
Error Codes:
| Code | Field | Description | Fix |
|---|---|---|---|
MISSING_REQUIRED_FIELD | (various) | Required field missing | Provide required field |
INVALID_FACT_ID_FORMAT | factId | Doesn't start with "fact-" | Use "fact-" prefix |
INVALID_ARRAY | (array fields) | Invalid array or element | Provide valid array |
EMPTY_ARRAY | (array fields) | Empty array | Provide at least one element |
INVALID_CONFIDENCE | confidence | Not between 0-100 | Use number 0-100 |
INVALID_FACT_TYPE | factType | Invalid fact type | Use valid fact type |
INVALID_SOURCE_TYPE | sourceType | Invalid source type | Use valid source type |
INVALID_EXPORT_FORMAT | format | Invalid export format | Use "json", "jsonld", or "csv" |
INVALID_SORT_BY | sortBy | Invalid sort field | Use valid sort field |
INVALID_TAG_MATCH | tagMatch | Invalid tag match mode | Use "any" or "all" |
INVALID_SORT_ORDER | sortOrder | Invalid sort order | Use "asc" or "desc" |
INVALID_DATE_RANGE | (date fields) | Invalid date range | Ensure start < end |
INVALID_VALIDITY_PERIOD | validFrom/validUntil | Invalid validity period | Ensure validFrom < validUntil |
INVALID_UPDATE | (update fields) | No fields to update | Provide at least one update field |
INVALID_CONSOLIDATION | factIds | Invalid consolidation params | Check requirements |
INVALID_METADATA | metadata, sourceRef | Invalid metadata structure | Provide valid object |
Valid Fact Types:
preferenceidentityknowledgerelationshipeventobservationcustom
Other Validation Errors
User Validation Error (src/users/validators.ts)
| Code | Description |
|---|---|
MISSING_REQUIRED_FIELD | Required field missing |
INVALID_FORMAT | Invalid field format |
Agent Validation Error (src/agents/validators.ts)
| Code | Description |
|---|---|
MISSING_REQUIRED_FIELD | Required field missing |
INVALID_FORMAT | Invalid field format |
Session Validation Error (src/sessions/validators.ts)
| Code | Description |
|---|---|
MISSING_SESSION_ID | sessionId required |
INVALID_SESSION_ID | Invalid sessionId format |
INVALID_STATUS | Invalid session status |
INVALID_FILTERS | Invalid filter combination |
Auth Validation Error (src/auth/validators.ts)
| Code | Description |
|---|---|
MISSING_AUTH_PROVIDER | Auth provider required |
MISSING_TENANT_ID | tenantId required |
Resilience Errors
Source: src/resilience/types.ts
CircuitOpenError
Thrown when: Circuit breaker is open (backend unhealthy)
class CircuitOpenError extends Error {
constructor(
message: string = "Circuit breaker is open - request rejected",
public readonly retryAfterMs?: number,
);
}
Recovery:
- Wait for
retryAfterMsmilliseconds - Retry the request
- Backend will eventually recover
Example:
import { CircuitOpenError } from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof CircuitOpenError) {
const delay = error.retryAfterMs || 30000;
console.log(`Circuit open, waiting ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
// Retry
}
}
RateLimitExceededError
Thrown when: Rate limit exceeded and waiting is disabled
class RateLimitExceededError extends Error {
constructor(
public readonly tokensAvailable: number,
public readonly refillInMs: number,
);
}
Recovery:
- Wait for
refillInMsmilliseconds - Token bucket will refill
- Retry the request
Example:
import { RateLimitExceededError } from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof RateLimitExceededError) {
console.log(`Rate limited, ${error.tokensAvailable} tokens available`);
console.log(`Refill in ${error.refillInMs}ms`);
await new Promise(resolve => setTimeout(resolve, error.refillInMs));
// Retry
}
}
QueueFullError
Thrown when: Priority queue is full for the given priority
class QueueFullError extends Error {
constructor(
public readonly priority: Priority,
public readonly queueSize: number,
);
}
Recovery:
- Reduce request volume
- Return 503 Service Unavailable
- Implement exponential backoff
Example:
import { QueueFullError } from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof QueueFullError) {
console.error(`Queue full for ${error.priority} priority`);
console.error(`Queue size: ${error.queueSize}`);
return { status: 503, message: 'Service overloaded' };
}
}
AcquireTimeoutError
Thrown when: Semaphore acquire times out
class AcquireTimeoutError extends Error {
constructor(
public readonly timeoutMs: number,
public readonly waitingCount: number,
);
}
Recovery:
- Increase timeout in resilience config
- Reduce concurrent requests
- Scale backend capacity
Graph Errors
Source: src/graph/types.ts
GraphDatabaseError
Base class for all graph errors
class GraphDatabaseError extends Error {
constructor(
message: string,
public readonly code?: string,
public readonly cause?: Error,
);
}
GraphConnectionError
Thrown when: Connection to graph database fails
class GraphConnectionError extends GraphDatabaseError {
constructor(message: string, cause?: Error);
}
Recovery:
- Check graph database is running
- Verify connection URI
- Check network connectivity
Example:
import { GraphConnectionError } from "@cortexmemory/sdk";
// Graph sync is automatic when CORTEX_GRAPH_SYNC=true (v0.29.0+)
// Graph failures are non-fatal - the app continues working
try {
await cortex.memory.remember({
// ... params
// Graph sync happens automatically if CORTEX_GRAPH_SYNC=true
});
} catch (error) {
if (error instanceof GraphConnectionError) {
console.error("Graph database unavailable");
// Graph failures are logged but don't block the operation
// The data is still stored in Convex (source of truth)
}
}
GraphAuthenticationError
Thrown when: Graph database authentication fails
class GraphAuthenticationError extends GraphConnectionError {
constructor(
message: string,
public readonly uri: string,
public readonly username: string,
cause?: Error,
);
}
Recovery:
- Check NEO4J_PASSWORD in .env
- Verify username
- Check database access permissions
Example:
import { GraphAuthenticationError } from "@cortexmemory/sdk";
try {
await graphAdapter.connect(config);
} catch (error) {
if (error instanceof GraphAuthenticationError) {
console.error("Authentication failed:");
console.error(` URI: ${error.uri}`);
console.error(` Username: ${error.username}`);
console.error("Check NEO4J_PASSWORD in .env");
// Disable graph sync by unsetting CORTEX_GRAPH_SYNC or setting to false
}
}
GraphQueryError
Thrown when: Graph query execution fails
class GraphQueryError extends GraphDatabaseError {
constructor(
message: string,
public readonly query?: string,
cause?: Error,
);
}
Recovery:
- Check query syntax
- Verify node/relationship exists
- Check database constraints
GraphNotFoundError
Thrown when: Node or edge not found
class GraphNotFoundError extends GraphDatabaseError {
constructor(resourceType: "node" | "edge" | "path", identifier: string);
}
Other Errors
A2ATimeoutError
Thrown when: A2A request times out waiting for response
Source: src/types/index.ts
class A2ATimeoutError extends Error {
public readonly name = "A2ATimeoutError";
constructor(
message: string,
public readonly messageId: string,
public readonly timeout: number,
);
}
Recovery:
- Increase timeout in A2ARequestParams
- Check receiving agent is responding
- Retry with exponential backoff
CascadeDeletionError
Thrown when: User cascade deletion encounters errors
Source: src/users/index.ts
class CascadeDeletionError extends Error {
constructor(
message: string,
public readonly cause?: unknown,
);
}
Recovery:
- Check the
causeproperty for underlying error details - Retry deletion operation
- Check permissions
AgentCascadeDeletionError
Thrown when: Agent cascade deletion encounters errors
Source: src/agents/index.ts
class AgentCascadeDeletionError extends Error {
constructor(
message: string,
public readonly cause?: unknown,
);
}
Recovery:
- Check the
causeproperty for underlying error details - Retry deletion operation
- Check permissions
ResumableStreamError
Thrown when: Streaming operation fails with resume context
Source: src/memory/streaming/ErrorRecovery.ts
class ResumableStreamError extends Error {
constructor(
public readonly originalError: Error,
public readonly resumeToken: string,
);
}
Recovery:
- Use
resumeTokento resume from failure point - Retry with stored progress
- Check
originalErrorfor underlying failure details
Troubleshooting Guide
Common Errors
"OWNER_REQUIRED"
Cause: Neither userId nor agentId provided
Fix:
// Wrong - no owner
await cortex.memory.remember({
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi",
});
// Correct - userId provided
await cortex.memory.remember({
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi",
userId: "user-123",
userName: "Alex",
agentId: "assistant",
});
"MISSING_REQUIRED_FIELD: userName"
Cause: userName required when userId is provided
Fix:
// Wrong - missing userName
await cortex.memory.remember({
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi",
userId: "user-123", // userName required!
agentId: "assistant",
});
// Correct
await cortex.memory.remember({
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi",
userId: "user-123",
userName: "Alex", // Added userName
agentId: "assistant",
});
"INVALID_IMPORTANCE"
Cause: Importance not between 0-100
Fix:
// Wrong
await cortex.memory.remember({
// ... other params
importance: 150, // Out of range!
});
// Correct
await cortex.memory.remember({
// ... other params
importance: 90, // 0-100
});
"Circuit breaker is open"
Cause: Backend is experiencing failures
Recovery Strategy:
import { CircuitOpenError } from "@cortexmemory/sdk";
async function rememberWithRetry(params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await cortex.memory.remember(params);
} catch (error) {
if (error instanceof CircuitOpenError) {
const delay = error.retryAfterMs || 30000 * (i + 1);
console.log(`Circuit open, retry ${i + 1}/${maxRetries} in ${delay}ms`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error("Max retries exceeded");
}
"Queue full for priority 'normal'"
Cause: System overloaded
Recovery Strategy:
- Reduce request volume:
// Use exponential backoff
const backoff = Math.min(1000 * Math.pow(2, attempt), 30000);
await new Promise((resolve) => setTimeout(resolve, backoff));
- Use higher priority for critical operations:
// Configure resilience with priority
const cortex = new CortexMemory({
// ... config
resilience: {
enabled: true,
// Critical operations get priority
},
});
- Return 503 to clients:
import { QueueFullError } from '@cortexmemory/sdk';
try {
await cortex.memory.remember({...});
} catch (error) {
if (error instanceof QueueFullError) {
return res.status(503).json({
error: 'Service temporarily unavailable',
retryAfter: 30,
});
}
}
"Graph authentication failed"
Cause: Invalid Neo4j credentials
Fix:
- Check
.envfile:
NEO4J_URI=neo4j://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your-password-here # Check this!
CORTEX_GRAPH_SYNC=true # Enable graph sync
- Disable graph if credentials are unavailable:
# In .env.local - disable graph sync
CORTEX_GRAPH_SYNC=false
import { GraphAuthenticationError } from "@cortexmemory/sdk";
// Graph sync is automatic when CORTEX_GRAPH_SYNC=true (v0.29.0+)
// If graph auth fails, the operation still succeeds (graph is optional)
try {
await cortex.memory.remember({
// ... params
// Graph sync happens automatically, failures are logged but non-fatal
});
} catch (error) {
if (error instanceof GraphAuthenticationError) {
console.error("Graph auth failed - check NEO4J_PASSWORD in .env");
// Data is still stored in Convex (source of truth)
}
}
"INVALID_FACT_TYPE"
Cause: Invalid fact type provided
Fix:
// Wrong
await cortex.facts.store({
// ... params
factType: "unknown", // Invalid!
});
// Correct - use valid fact type
await cortex.facts.store({
// ... params
factType: "preference", // Valid
});
// Valid fact types:
// - preference
// - identity
// - knowledge
// - relationship
// - event
// - observation
// - custom
Error Recovery Strategies
Retry with Exponential Backoff
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000,
): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries - 1) {
throw error;
}
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
// Usage
await withRetry(() => cortex.memory.remember({...}));
Circuit Breaker Pattern
import { CircuitOpenError } from '@cortexmemory/sdk';
class CircuitBreakerWrapper {
private failures = 0;
private lastFailure?: number;
private readonly threshold = 5;
private readonly timeout = 60000; // 1 minute
async execute<T>(operation: () => Promise<T>): Promise<T> {
// Check if circuit should be open
if (this.failures >= this.threshold) {
const elapsed = Date.now() - (this.lastFailure || 0);
if (elapsed < this.timeout) {
throw new CircuitOpenError(
'Circuit breaker open',
this.timeout - elapsed,
);
}
// Try half-open
this.failures = this.threshold - 1;
}
try {
const result = await operation();
this.failures = 0; // Success resets failures
return result;
} catch (error) {
this.failures++;
this.lastFailure = Date.now();
throw error;
}
}
}
// Usage
const breaker = new CircuitBreakerWrapper();
await breaker.execute(() => cortex.memory.remember({...}));
Graceful Degradation
import { GraphConnectionError, CircuitOpenError } from "@cortexmemory/sdk";
// v0.29.0+: Graph sync is automatic when CORTEX_GRAPH_SYNC=true
// Graph failures are non-fatal - data is still stored in Convex
async function rememberWithFallback(params: RememberParams) {
try {
// Graph sync happens automatically if CORTEX_GRAPH_SYNC=true
return await cortex.memory.remember({
...params,
extractFacts: true,
});
} catch (error) {
if (error instanceof GraphConnectionError) {
// Graph failures are logged but don't block the operation
console.warn("Graph unavailable - data still saved to Convex");
// The operation already succeeded for Convex
}
if (error instanceof CircuitOpenError) {
// Fallback: minimal storage
console.warn("Backend stressed, using minimal storage");
return await cortex.memory.remember({
...params,
skipLayers: ["facts", "graph"],
});
}
throw error;
}
}
See Also
- Types & Interfaces - Type definitions
- API Reference - API methods
- Resilience Layer - Overload protection
- Core Concepts - Understanding errors