Skip to main content

Error Handling

Learn how to handle errors gracefully in your Flow API integration.

Error Types

Validation Errors (400)

Invalid request data:

try {
await flow.posts.create({
channelId: '', // Invalid: empty
content: 'Hello',
});
} catch (error) {
if (error instanceof FlowError && error.status === 400) {
error.details?.forEach(detail => {
console.error(`${detail.field}: ${detail.message}`);
});
}
}

Authentication Errors (401)

Invalid or missing API key:

try {
await flow.posts.list();
} catch (error) {
if (error instanceof FlowError && error.status === 401) {
console.error('Invalid API key');
// Regenerate API key or check configuration
}
}

Authorization Errors (403)

Insufficient permissions:

try {
await flow.posts.create({ channelId: 'channel_123', content: 'Hello' });
} catch (error) {
if (error instanceof FlowError && error.status === 403) {
console.error('Insufficient permissions');
// Check API key permissions
}
}

Not Found Errors (404)

Resource not found:

try {
await flow.posts.get('invalid_id');
} catch (error) {
if (error instanceof FlowError && error.status === 404) {
console.error('Post not found');
// Handle missing resource
}
}

Rate Limit Errors (429)

Rate limit exceeded:

try {
await flow.posts.list();
} catch (error) {
if (error instanceof FlowError && error.status === 429) {
const waitTime = error.retryAfter || 60;
console.log(`Rate limited. Wait ${waitTime} seconds`);
await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
// Retry request
}
}

Server Errors (500, 503)

Internal server errors:

try {
await flow.posts.create({ channelId: 'channel_123', content: 'Hello' });
} catch (error) {
if (error instanceof FlowError && (error.status === 500 || error.status === 503)) {
// Retry with exponential backoff
await retryWithBackoff(() =>
flow.posts.create({ channelId: 'channel_123', content: 'Hello' })
);
}
}

Retry Logic

Exponential Backoff

async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error instanceof FlowError) {
// Don't retry client errors
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
throw error;
}

// Retry server errors and rate limits
if (error.status >= 500 || error.status === 429 || error.status === 503) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
const retryAfter = error.retryAfter ? error.retryAfter * 1000 : delay;
await new Promise(resolve => setTimeout(resolve, retryAfter));
continue;
}
}
throw error;
}
}
throw new Error('Max retries exceeded');
}

Usage

const post = await retryWithBackoff(() =>
flow.posts.create({
channelId: 'channel_123',
content: 'Hello!',
})
);

Error Logging

Log Request IDs

try {
await flow.posts.create({ channelId: 'channel_123', content: 'Hello' });
} catch (error) {
if (error instanceof FlowError && error.requestId) {
console.error(`Request ID: ${error.requestId}`, error);
// Include request_id when contacting support
}
}

Structured Logging

function logError(error: FlowError, context: Record<string, any>) {
logger.error({
error: {
type: error.type,
code: error.code,
message: error.message,
status: error.status,
requestId: error.requestId,
details: error.details,
},
context,
});
}

Best Practices

  1. Always handle errors - Never ignore errors
  2. Use typed errors - Use SDK error types when available
  3. Implement retries - For transient errors (429, 500, 503)
  4. Log request IDs - For debugging and support
  5. Validate before sending - Prevent validation errors
  6. Monitor error rates - Alert on high error rates

Next Steps