tessl install tessl/npm-langsmith@0.4.3TypeScript client SDK for the LangSmith LLM tracing, evaluation, and monitoring platform.
Comprehensive guide to handling errors in LangSmith SDK operations.
The LangSmith SDK can encounter various errors during API operations. This guide provides systematic error handling patterns, HTTP status code meanings, recovery strategies, and production-ready error handling code.
| Code | Meaning | Common Causes | Recovery Strategy |
|---|---|---|---|
| 200 | Success | Request completed successfully | Continue normally |
| 400 | Bad Request | Invalid parameters, malformed data, validation errors | Validate inputs, check parameter types and formats |
| 401 | Unauthorized | Missing API key, invalid API key, expired key | Check LANGCHAIN_API_KEY env var, regenerate key |
| 403 | Forbidden | Insufficient permissions, wrong workspace | Verify API key scopes, check workspace ID |
| 404 | Not Found | Resource doesn't exist (run, dataset, project) | Check IDs, verify resource was created |
| 409 | Conflict | Resource already exists, concurrent modification | Use upsert flag, check existence first, implement optimistic locking |
| 429 | Rate Limited | Too many requests in time window | Implement exponential backoff, reduce request rate, increase delays |
| 500 | Internal Server Error | LangSmith service issue | Retry with backoff, check status page, contact support |
| 502 | Bad Gateway | Proxy/gateway issue | Retry with backoff |
| 503 | Service Unavailable | Temporary service outage, maintenance | Retry with backoff, implement fallback, check status |
| 504 | Gateway Timeout | Request took too long | Retry, increase timeout, simplify request |
Handle common error scenarios with appropriate messages.
import { Client } from "langsmith";
const client = new Client();
try {
const run = await client.readRun(runId);
console.log(run.name);
} catch (error) {
// Type-specific handling
if (error.status === 404) {
console.error("Run not found - verify run ID:", runId);
} else if (error.status === 401) {
console.error("Authentication failed - check LANGCHAIN_API_KEY environment variable");
} else if (error.status === 429) {
console.error("Rate limit exceeded - reduce request frequency");
} else if (error.status >= 500) {
console.error("Server error - LangSmith service may be experiencing issues");
} else {
console.error("API error:", error.message);
}
}Automatically retry transient errors with increasing delays.
import { Client } from "langsmith";
import type { Run } from "langsmith/schemas";
async function readRunWithRetry(
client: Client,
runId: string,
maxRetries = 3
): Promise<Run> {
for (let i = 0; i < maxRetries; i++) {
try {
return await client.readRun(runId);
} catch (error) {
// Don't retry on permanent errors
if (error.status === 404 || error.status === 401 || error.status === 403) {
throw error; // Permanent failure - don't retry
}
// Retry on rate limits and transient errors
if (i < maxRetries - 1 && (error.status === 429 || error.status >= 500)) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff: 1s, 2s, 4s
console.log(`Retry attempt ${i + 1}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Max retries exceeded or non-retryable error
throw error;
}
}
throw new Error("Max retries exceeded");
}
// Usage
try {
const run = await readRunWithRetry(client, runId);
console.log("Run retrieved:", run.name);
} catch (error) {
console.error("Failed after retries:", error.message);
}Retry Configuration:
// Configurable retry logic
interface RetryConfig {
maxRetries: number;
baseDelay: number;
maxDelay: number;
retryableStatuses: number[];
}
const defaultRetryConfig: RetryConfig = {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 30000,
retryableStatuses: [429, 500, 502, 503, 504]
};
async function withRetry<T>(
operation: () => Promise<T>,
config: RetryConfig = defaultRetryConfig
): Promise<T> {
for (let i = 0; i < config.maxRetries; i++) {
try {
return await operation();
} catch (error) {
const shouldRetry =
i < config.maxRetries - 1 &&
config.retryableStatuses.includes(error.status);
if (!shouldRetry) {
throw error;
}
const delay = Math.min(
Math.pow(2, i) * config.baseDelay,
config.maxDelay
);
console.log(`Retrying after ${delay}ms (attempt ${i + 1}/${config.maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error("Max retries exceeded");
}
// Usage
const run = await withRetry(() => client.readRun(runId));Continue operation even if tracing fails.
async function traceWithFallback(operation: () => Promise<any>) {
const client = new Client();
try {
// Attempt to create trace
await client.createRun({
name: "operation",
run_type: "chain",
inputs: {},
start_time: Date.now(),
});
const result = await operation();
await client.updateRun(runId, {
end_time: Date.now(),
outputs: { result }
});
return result;
} catch (error) {
// If tracing fails, log warning but continue operation
console.warn("Tracing unavailable, continuing without tracing:", error.message);
// Still execute the operation
return await operation();
}
}
// Usage
const result = await traceWithFallback(async () => {
return await criticalBusinessLogic();
});Optional Tracing Pattern:
import { Client } from "langsmith";
class OptionalTracingClient {
private client?: Client;
private enabled: boolean;
constructor() {
try {
this.client = new Client();
this.enabled = !!process.env.LANGCHAIN_API_KEY;
} catch (error) {
console.warn("LangSmith client initialization failed:", error.message);
this.enabled = false;
}
}
async createRun(run: any) {
if (!this.enabled || !this.client) return;
try {
await this.client.createRun(run);
} catch (error) {
console.warn("Failed to create run, continuing:", error.message);
}
}
async flush() {
if (!this.enabled || !this.client) return;
try {
await this.client.awaitPendingTraceBatches();
} catch (error) {
console.warn("Failed to flush traces:", error.message);
}
}
}
// Usage
const tracingClient = new OptionalTracingClient();
await tracingClient.createRun({...}); // Never throwsHandle errors during iteration without stopping the loop.
import { Client } from "langsmith";
async function safeListRuns(client: Client, projectName: string) {
try {
for await (const run of client.listRuns({ projectName })) {
try {
// Process each run safely
await processRun(run);
} catch (error) {
// GOOD: Handle per-run errors without stopping iteration
console.error(`Error processing run ${run.id}:`, error.message);
// Continue to next run
}
}
} catch (error) {
// Handle iteration-level errors (project not found, auth failure)
if (error.status === 404) {
console.error("Project not found:", projectName);
} else if (error.status === 401) {
console.error("Authentication failed - check API key");
} else {
console.error("Failed to list runs:", error.message);
}
// Optionally rethrow if critical
throw error;
}
}
// Usage
try {
await safeListRuns(client, "my-project");
console.log("All runs processed successfully");
} catch (error) {
console.error("Fatal error during run processing");
}Prevent out-of-memory errors when querying large result sets.
import { Client } from "langsmith";
import type { Run } from "langsmith/schemas";
async function getAllRuns(
client: Client,
projectName: string,
maxRuns = 10000
): Promise<Run[]> {
const runs: Run[] = [];
try {
for await (const run of client.listRuns({ projectName, limit: maxRuns })) {
runs.push(run);
// Safety limit to prevent memory issues
if (runs.length >= maxRuns) {
console.warn(`Reached maximum run limit of ${maxRuns}`);
break;
}
}
} catch (error) {
console.error("Error fetching runs:", error.message);
// Return partial results rather than failing completely
console.log(`Returning ${runs.length} runs before error`);
}
return runs;
}
// Usage with processing
async function processAllRuns(client: Client, projectName: string) {
const runs = await getAllRuns(client, projectName, 5000);
console.log(`Processing ${runs.length} runs`);
for (const run of runs) {
await processRun(run);
}
}Streaming Alternative (Better for Large Sets):
async function processRunsStreaming(client: Client, projectName: string) {
let processedCount = 0;
const maxProcessed = 10000;
try {
for await (const run of client.listRuns({ projectName })) {
await processRun(run); // Process one at a time
processedCount++;
if (processedCount >= maxProcessed) {
console.warn(`Processed maximum ${maxProcessed} runs`);
break;
}
// Progress logging
if (processedCount % 100 === 0) {
console.log(`Processed ${processedCount} runs...`);
}
}
} catch (error) {
console.error(`Error after processing ${processedCount} runs:`, error.message);
}
return processedCount;
}Validate inputs before making API requests.
import { Client } from "langsmith";
async function createRunSafely(client: Client, params: any) {
// Validate required fields
if (!params.name) {
throw new Error("Run name is required");
}
if (!params.run_type) {
throw new Error("Run type is required");
}
// Validate run_type value
const validRunTypes = ["llm", "chain", "tool", "retriever", "embedding", "prompt", "parser"];
if (!validRunTypes.includes(params.run_type)) {
throw new Error(`Invalid run_type: ${params.run_type}. Must be one of: ${validRunTypes.join(", ")}`);
}
// Validate timestamps
if (params.start_time && params.end_time && params.start_time > params.end_time) {
throw new Error("start_time cannot be greater than end_time");
}
try {
await client.createRun(params);
} catch (error) {
if (error.status === 400) {
console.error("Validation failed on server:", error.message);
console.error("Params:", JSON.stringify(params, null, 2));
}
throw error;
}
}Handle long-running operations that may timeout.
import { Client } from "langsmith";
// Configure custom timeout
const client = new Client({
timeout_ms: 30000 // 30 seconds
});
async function operationWithTimeout(client: Client, operation: () => Promise<any>) {
try {
return await operation();
} catch (error) {
if (error.code === 'ETIMEDOUT' || error.code === 'ESOCKETTIMEDOUT') {
console.error("Operation timed out - consider:");
console.error("1. Increasing timeout_ms in client config");
console.error("2. Breaking operation into smaller chunks");
console.error("3. Using pagination/limits");
throw new Error("Operation timeout");
}
throw error;
}
}Handle errors in batch operations.
import { Client } from "langsmith";
async function batchIngestWithErrorHandling(client: Client, runs: any[]) {
// Validate batch size
if (runs.length > 1000) {
console.warn("Large batch - consider splitting into smaller chunks");
}
try {
await client.batchIngestRuns({
post: runs
});
console.log(`Successfully ingested ${runs.length} runs`);
} catch (error) {
if (error.status === 413) {
// Payload too large
console.error("Batch too large, splitting into chunks...");
const chunkSize = Math.ceil(runs.length / 2);
const chunks = [];
for (let i = 0; i < runs.length; i += chunkSize) {
chunks.push(runs.slice(i, i + chunkSize));
}
for (const chunk of chunks) {
await batchIngestWithErrorHandling(client, chunk); // Recursive
}
} else if (error.status === 400) {
console.error("Validation error in batch:");
console.error("Check that all runs have required fields");
throw error;
} else {
throw error;
}
}
}Possible Causes:
Solutions:
// 1. Verify API key
const config = Client.getDefaultClientConfig();
console.log("API URL:", config.apiUrl);
console.log("API Key configured:", !!config.apiKey);
console.log("API Key prefix:", config.apiKey?.substring(0, 8) + "...");
// 2. Ensure flush before exit
await client.awaitPendingTraceBatches();
// 3. Test connectivity
try {
const projects = await client.listProjects({ limit: 1 });
console.log("API connectivity: OK");
} catch (error) {
console.error("Cannot reach LangSmith API:", error.message);
}
// 4. Verify project name
const projectName = process.env.LANGCHAIN_PROJECT || "default";
console.log("Using project:", projectName);
// 5. Check sampling
const client = new Client({
tracingSamplingRate: 1.0 // Force 100% for debugging
});Cause: Using wrong import path.
Don't:
// ERROR: traceable is not exported from main package
import { traceable } from "langsmith";Do:
// CORRECT: Use subpath export
import { traceable } from "langsmith/traceable";All Subpath Exports:
import { Client, RunTree, Cache, uuid7 } from "langsmith";
import { traceable, getCurrentRunTree } from "langsmith/traceable";
import { evaluate, evaluateComparative } from "langsmith/evaluation";
import { wrapOpenAI } from "langsmith/wrappers/openai";
import { wrapAnthropic } from "langsmith/wrappers/anthropic";
import { wrapAISDK } from "langsmith/experimental/vercel";
import { createAnonymizer } from "langsmith/anonymizer";
import { getLangchainCallbacks } from "langsmith/langchain";
import { test } from "langsmith/jest"; // or langsmith/vitestCause: Dataset doesn't exist or wrong identifier.
Solution:
import { Client } from "langsmith";
const client = new Client();
// Check existence before using
async function getOrCreateDataset(name: string) {
try {
// Try to read existing dataset
return await client.readDataset({ datasetName: name });
} catch (error) {
if (error.status === 404) {
// Dataset doesn't exist - create it
console.log(`Dataset '${name}' not found, creating...`);
return await client.createDataset({
datasetName: name,
description: "Auto-created dataset"
});
}
throw error; // Other error - rethrow
}
}
// Or use hasDataset
async function ensureDataset(name: string) {
const exists = await client.hasDataset({ datasetName: name });
if (!exists) {
await client.createDataset({ datasetName: name });
}
}Cause: Run doesn't exist or wrong ID.
Solution:
async function readRunSafely(client: Client, runId: string) {
try {
return await client.readRun(runId);
} catch (error) {
if (error.status === 404) {
console.error(`Run ${runId} not found. Possible reasons:`);
console.error("1. Run hasn't been created yet");
console.error("2. Run was deleted");
console.error("3. Wrong run ID");
console.error("4. Run in different workspace");
return null;
}
throw error;
}
}Cause: Missing or invalid API key.
Solution:
import { Client } from "langsmith";
// Validate API key at startup
function validateLangSmithConfig() {
const apiKey = process.env.LANGCHAIN_API_KEY;
if (!apiKey) {
throw new Error(
"LANGCHAIN_API_KEY environment variable is not set. " +
"Get your API key from https://smith.langchain.com/settings"
);
}
if (!apiKey.startsWith("lsv2_")) {
console.warn(
"API key doesn't start with 'lsv2_' - may be invalid or outdated format"
);
}
return apiKey;
}
// Use at application startup
validateLangSmithConfig();
const client = new Client();
// Or: Test API key validity
async function testAPIKey(client: Client) {
try {
await client.listProjects({ limit: 1 });
console.log("API key is valid");
return true;
} catch (error) {
if (error.status === 401) {
console.error("API key is invalid or expired");
return false;
}
throw error;
}
}Cause: Too many requests in short time.
Solution:
import { Client } from "langsmith";
class RateLimitedClient {
private client: Client;
private requestQueue: Array<() => Promise<any>> = [];
private processing = false;
private requestsPerSecond: number;
constructor(requestsPerSecond = 10) {
this.client = new Client();
this.requestsPerSecond = requestsPerSecond;
}
async queueRequest<T>(operation: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.requestQueue.push(async () => {
try {
const result = await operation();
resolve(result);
} catch (error) {
reject(error);
}
});
if (!this.processing) {
this.processQueue();
}
});
}
private async processQueue() {
this.processing = true;
const delayMs = 1000 / this.requestsPerSecond;
while (this.requestQueue.length > 0) {
const request = this.requestQueue.shift();
if (request) {
await request();
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
this.processing = false;
}
}
// Usage
const rateLimitedClient = new RateLimitedClient(10); // 10 requests/sec
for (const item of items) {
await rateLimitedClient.queueRequest(() =>
client.createFeedback(item.runId, "rating", { score: 1 })
);
}Cause: Project doesn't exist or wrong name/ID.
Solution:
async function ensureProject(client: Client, projectName: string) {
try {
// Check if project exists
const project = await client.readProject({ projectName });
return project;
} catch (error) {
if (error.status === 404) {
// Create project if it doesn't exist
console.log(`Project '${projectName}' not found, creating...`);
return await client.createProject({
projectName,
description: "Auto-created project"
});
}
throw error;
}
}
// Or use hasProject
async function getOrCreateProject(client: Client, name: string) {
const exists = await client.hasProject({ projectName: name });
if (!exists) {
return await client.createProject({
projectName: name,
description: "Auto-created project"
});
}
return await client.readProject({ projectName: name });
}Cause: Batching enabled but not flushed.
Solution:
import { Client } from "langsmith";
const client = new Client({
autoBatchTracing: true
});
// Execute traced operations
await myTracedFunction();
// CRITICAL: Flush before important operations or exit
await client.awaitPendingTraceBatches();
// For debugging: disable batching temporarily
const debugClient = new Client({
autoBatchTracing: false // Immediate upload
});Cause: Calling getCurrentRunTree() outside traceable context.
Solution:
import { traceable, getCurrentRunTree } from "langsmith/traceable";
// Don't call outside traceable
function helper() {
// ERROR: Not inside traceable context
const runTree = getCurrentRunTree(); // Throws error
}
// SOLUTION 1: Use optional variant
function safeHelper() {
const runTree = getCurrentRunTree(true); // Returns undefined if not in context
if (runTree) {
runTree.metadata = { helper: true };
}
}
// SOLUTION 2: Only call within traceable
const myFunction = traceable(async () => {
const runTree = getCurrentRunTree(); // OK: Inside traceable
runTree.metadata = { processed: true };
}, { name: "my-function" });Cause: Cannot reach LangSmith API.
Solution:
import { Client } from "langsmith";
async function checkConnectivity() {
const client = new Client({
timeout_ms: 5000 // Short timeout for connectivity check
});
try {
await client.listProjects({ limit: 1 });
console.log("✓ Connected to LangSmith API");
return true;
} catch (error) {
if (error.code === 'ECONNREFUSED') {
console.error("✗ Cannot connect to LangSmith API");
console.error("Check:");
console.error(" 1. Internet connectivity");
console.error(" 2. Firewall/proxy settings");
console.error(" 3. API URL:", client.getHostUrl());
} else if (error.code === 'ETIMEDOUT') {
console.error("✗ Connection timeout");
console.error(" Consider increasing timeout_ms or checking network");
} else {
console.error("✗ Connection error:", error.message);
}
return false;
}
}
// Use at startup
if (!(await checkConnectivity())) {
console.log("Running in offline mode - tracing disabled");
}Cause: Accumulating too many runs or examples in memory.
Solution:
import { Client } from "langsmith";
// BAD: Accumulates all in memory
const allRuns = [];
for await (const run of client.listRuns({ projectName: "huge" })) {
allRuns.push(run); // OOM risk
}
// GOOD: Process and discard
for await (const run of client.listRuns({ projectName: "huge" })) {
await processRun(run);
// run is eligible for garbage collection after processing
}
// GOOD: Use limits
const runs = [];
for await (const run of client.listRuns({
projectName: "huge",
limit: 1000 // Explicit limit
})) {
runs.push(run);
}
// GOOD: Batch processing with limits
const batchSize = 100;
let batch = [];
for await (const run of client.listRuns({ projectName: "project" })) {
batch.push(run);
if (batch.length >= batchSize) {
await processBatch(batch);
batch = []; // Clear batch to free memory
}
}
// Process remaining
if (batch.length > 0) {
await processBatch(batch);
}Prevent cascading failures by stopping requests after repeated errors.
class CircuitBreaker {
private failureCount = 0;
private failureThreshold = 5;
private resetTimeout = 60000; // 1 minute
private state: 'closed' | 'open' | 'half-open' = 'closed';
private lastFailure?: number;
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
const timeSinceFailure = Date.now() - (this.lastFailure || 0);
if (timeSinceFailure < this.resetTimeout) {
throw new Error("Circuit breaker is OPEN - too many failures");
}
this.state = 'half-open';
}
try {
const result = await operation();
// Success - reset if in half-open
if (this.state === 'half-open') {
this.state = 'closed';
this.failureCount = 0;
}
return result;
} catch (error) {
this.failureCount++;
this.lastFailure = Date.now();
// Open circuit if threshold exceeded
if (this.failureCount >= this.failureThreshold) {
this.state = 'open';
console.error(`Circuit breaker opened after ${this.failureCount} failures`);
}
throw error;
}
}
}
// Usage
const breaker = new CircuitBreaker();
const client = new Client();
async function robustCreateRun(params: any) {
return await breaker.execute(() => client.createRun(params));
}Continue operation with local logging if LangSmith unavailable.
import { Client } from "langsmith";
import * as fs from "fs";
class ResilientTracer {
private client: Client;
private fallbackPath: string;
private langsmithAvailable = true;
constructor(fallbackPath = "./traces.jsonl") {
this.client = new Client();
this.fallbackPath = fallbackPath;
}
async createRun(run: any) {
if (this.langsmithAvailable) {
try {
await this.client.createRun(run);
return;
} catch (error) {
console.warn("LangSmith unavailable, falling back to local logging");
this.langsmithAvailable = false;
// Fall through to local logging
}
}
// Fallback: log to local file
fs.appendFileSync(
this.fallbackPath,
JSON.stringify(run) + "\n"
);
}
async flush() {
if (this.langsmithAvailable) {
await this.client.awaitPendingTraceBatches();
}
}
}const client = new Client({
debug: true // Logs all HTTP requests
});
// See full request/response details in console
await client.createRun({...});import { Client } from "langsmith";
const client = new Client();
try {
await client.createRun({...});
} catch (error) {
console.error("=== LangSmith Error Details ===");
console.error("Status:", error.status);
console.error("Message:", error.message);
console.error("Method:", error.config?.method);
console.error("URL:", error.config?.url);
console.error("Data:", JSON.stringify(error.config?.data, null, 2));
console.error("Response:", error.response?.data);
console.error("================================");
throw error;
}// GOOD: Handle known error scenarios
try {
const dataset = await client.readDataset({ datasetName });
} catch (error) {
if (error.status === 404) {
// Expected: create dataset
await client.createDataset({ datasetName });
} else if (error.status === 401) {
// Expected: auth issue
throw new Error("Invalid API key - check configuration");
} else {
// Unexpected: log for debugging
console.error("Unexpected error:", error);
throw error;
}
}// GOOD: Contextual error messages
try {
await client.createExample({
dataset_id: datasetId,
inputs: exampleInputs,
outputs: exampleOutputs
});
} catch (error) {
throw new Error(
`Failed to create example in dataset ${datasetId}: ${error.message}`
);
}// BAD: May log API key
console.error("Error with config:", clientConfig);
// GOOD: Sanitize before logging
const safeConfig = {
...clientConfig,
apiKey: clientConfig.apiKey ? "[REDACTED]" : undefined
};
console.error("Error with config:", safeConfig);function isLangSmithError(error: any): error is { status: number; message: string } {
return typeof error === 'object' && 'status' in error;
}
try {
await client.createRun({...});
} catch (error) {
if (isLangSmithError(error)) {
console.error(`API error ${error.status}:`, error.message);
} else {
console.error("Unexpected error:", error);
}
}When encountering errors, check: