Skip to main content

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:

  1. Validation Errors - Client-side validation (instant feedback)
  2. Resilience Errors - Overload protection (rate limits, circuit breaker)
  3. Graph Errors - Graph database operations
  4. Cascade Errors - Deletion cascade operations
  5. 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

  1. Error Class Hierarchy
  2. Error Handling Patterns
  3. Validation Errors
  4. Resilience Errors
  5. Graph Errors
  6. Other Errors
  7. 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:

CodeFieldDescriptionFix
MISSING_REQUIRED_FIELDmemorySpaceIdMissing or empty memorySpaceIdProvide valid memorySpaceId
MISSING_REQUIRED_FIELDconversationIdMissing or empty conversationIdProvide valid conversationId
MISSING_REQUIRED_FIELDuserMessageMissing or empty userMessageProvide message content
MISSING_REQUIRED_FIELDagentResponseMissing or empty agentResponseProvide response content
MISSING_REQUIRED_FIELDuserNameRequired when userId providedProvide userName with userId
OWNER_REQUIREDuserId/agentIdNeither userId nor agentId providedProvide at least one owner ID
INVALID_IMPORTANCEimportanceNot between 0-100Use number 0-100
INVALID_ID_FORMAT(any ID field)Invalid characters in IDUse alphanumeric, hyphens, underscores
INVALID_FORMATcontentTypeInvalid contentTypeUse "raw" or "summarized"
INVALID_SOURCE_TYPEsourceTypeInvalid sourceTypeUse "conversation", "system", "tool", or "a2a"
INVALID_EMBEDDINGembeddingInvalid embedding arrayProvide array of finite numbers
INVALID_TIMESTAMP(timestamp fields)Invalid timestampUse valid number or Date
EMPTY_ARRAYmessageIdsEmpty arrayProvide at least one element
MISSING_CONVERSATION_REFconversationRefRequired for conversation sourceProvide conversationRef
INVALID_STREAMresponseStreamInvalid stream objectUse ReadableStream or AsyncIterable
INVALID_FILTER(filter)Insufficient filter criteriaAdd userId or sourceType filter
INVALID_CONFIDENCEminConfidenceNot between 0-100Use number 0-100
INVALID_DATE_RANGEcreatedAfter/createdBeforeInvalid date rangeEnsure after < before
INVALID_GRAPH_DEPTHgraphExpansion.maxDepthNot between 1-5Use depth 1-5
NEGATIVE_NUMBERlimit, versionNegative or invalid numberUse 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:

CodeFieldDescriptionFix
MISSING_REQUIRED_FIELD(various)Required field missingProvide required field
INVALID_TYPEtypeInvalid conversation typeUse "user-agent" or "agent-agent"
INVALID_ROLEroleInvalid message roleUse "user", "agent", or "system"
INVALID_ID_FORMATconversationId, messageIdInvalid ID formatRemove invalid characters
INVALID_FORMATformatInvalid export formatUse "json" or "csv"
INVALID_SORT_ORDERsortOrderInvalid sort orderUse "asc" or "desc"
EMPTY_STRINGqueryEmpty search queryProvide non-empty query
INVALID_RANGElimit, offsetInvalid rangeUse valid positive integers
EMPTY_ARRAY(array fields)Empty arrayProvide at least one element
INVALID_ARRAY_LENGTH(array fields)Array length constraint violatedCheck min/max requirements
INVALID_DATE_RANGE(date fields)Start >= EndEnsure start < end
INVALID_PARTICIPANTSparticipantsInvalid participants structureMatch type requirements
DUPLICATE_VALUES(array fields)Duplicate values in arrayRemove duplicates

Facts Validation Errors

Class: FactsValidationError

Source: src/facts/validators.ts

Error Codes:

CodeFieldDescriptionFix
MISSING_REQUIRED_FIELD(various)Required field missingProvide required field
INVALID_FACT_ID_FORMATfactIdDoesn't start with "fact-"Use "fact-" prefix
INVALID_ARRAY(array fields)Invalid array or elementProvide valid array
EMPTY_ARRAY(array fields)Empty arrayProvide at least one element
INVALID_CONFIDENCEconfidenceNot between 0-100Use number 0-100
INVALID_FACT_TYPEfactTypeInvalid fact typeUse valid fact type
INVALID_SOURCE_TYPEsourceTypeInvalid source typeUse valid source type
INVALID_EXPORT_FORMATformatInvalid export formatUse "json", "jsonld", or "csv"
INVALID_SORT_BYsortByInvalid sort fieldUse valid sort field
INVALID_TAG_MATCHtagMatchInvalid tag match modeUse "any" or "all"
INVALID_SORT_ORDERsortOrderInvalid sort orderUse "asc" or "desc"
INVALID_DATE_RANGE(date fields)Invalid date rangeEnsure start < end
INVALID_VALIDITY_PERIODvalidFrom/validUntilInvalid validity periodEnsure validFrom < validUntil
INVALID_UPDATE(update fields)No fields to updateProvide at least one update field
INVALID_CONSOLIDATIONfactIdsInvalid consolidation paramsCheck requirements
INVALID_METADATAmetadata, sourceRefInvalid metadata structureProvide valid object

Valid Fact Types:

  • preference
  • identity
  • knowledge
  • relationship
  • event
  • observation
  • custom

Other Validation Errors

User Validation Error (src/users/validators.ts)

CodeDescription
MISSING_REQUIRED_FIELDRequired field missing
INVALID_FORMATInvalid field format

Agent Validation Error (src/agents/validators.ts)

CodeDescription
MISSING_REQUIRED_FIELDRequired field missing
INVALID_FORMATInvalid field format

Session Validation Error (src/sessions/validators.ts)

CodeDescription
MISSING_SESSION_IDsessionId required
INVALID_SESSION_IDInvalid sessionId format
INVALID_STATUSInvalid session status
INVALID_FILTERSInvalid filter combination

Auth Validation Error (src/auth/validators.ts)

CodeDescription
MISSING_AUTH_PROVIDERAuth provider required
MISSING_TENANT_IDtenantId 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 retryAfterMs milliseconds
  • 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 refillInMs milliseconds
  • 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 cause property 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 cause property 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 resumeToken to resume from failure point
  • Retry with stored progress
  • Check originalError for 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:

  1. Reduce request volume:
// Use exponential backoff
const backoff = Math.min(1000 * Math.pow(2, attempt), 30000);
await new Promise((resolve) => setTimeout(resolve, backoff));
  1. Use higher priority for critical operations:
// Configure resilience with priority
const cortex = new CortexMemory({
// ... config
resilience: {
enabled: true,
// Critical operations get priority
},
});
  1. 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:

  1. Check .env file:
NEO4J_URI=neo4j://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your-password-here # Check this!
CORTEX_GRAPH_SYNC=true # Enable graph sync
  1. 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