Error Codes Reference
Fresh All Notion API error codes with causes and solutions.Error Response Structure
javascript
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "Description of the error",
"request_id": "xxx-xxx-xxx"
}HTTP Status Codes
| Status | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid request body, missing required fields |
| 401 | Unauthorized | Invalid or missing API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist or not accessible |
| 409 | Conflict | Resource already exists |
| 429 | Too Many Requests | Rate limited |
| 500 | Internal Server Error | Notion server issue |
| 502 | Bad Gateway | Notion infrastructure issue |
| 503 | Service Unavailable | Notion temporarily unavailable |
| 504 | Gateway Timeout | Request took too long |
Error Codes by Category
Detailed Error Reference
Authentication Errors (401)
unauthorized
Cause: Invalid or missing API key.
javascript
{
"status": 401,
"code": "unauthorized",
"message": "API token is invalid."
}Solutions:
- Verify API key starts with
secret_ - Check for extra whitespace
- Regenerate key in integration settings
- Ensure key is for correct workspace
javascript
// Wrong
const notion = new Client({ auth: 'secret_xxx ' }); // trailing space
// Correct
const notion = new Client({ auth: process.env.NOTION_API_KEY.trim() });Authorization Errors (403)
restricted_resource
Cause: Integration lacks permission for this resource.
javascript
{
"status": 403,
"code": "restricted_resource",
"message": "Cannot access this resource."
}Solutions:
- Share the page/database with your integration
- Enable required capabilities in integration settings
- Check parent page permissions
object_not_found
Cause: Resource doesn't exist OR integration doesn't have access.
javascript
{
"status": 404,
"code": "object_not_found",
"message": "Could not find page with ID: xxx"
}Solutions:
- Verify the ID is correct
- Add integration connection to the page
- Check if page was deleted or archived
- Ensure ID format is correct (with or without dashes)
javascript
// Debug: Check if integration can see the resource
try {
await notion.pages.retrieve({ page_id: id });
} catch (error) {
if (error.code === 'object_not_found') {
console.log('Page not shared with integration or does not exist');
}
}Validation Errors (400)
validation_error
Cause: Request body doesn't match expected schema.
javascript
{
"status": 400,
"code": "validation_error",
"message": "body failed validation: body.properties.Title.title should be defined"
}Common Causes:
- Missing required properties
- Wrong property type
- Invalid enum value
- Exceeding limits
Solutions:
javascript
// Wrong: Missing title wrapper
properties: {
Name: { text: { content: 'Title' } }
}
// Correct: Title property needs title array
properties: {
Name: {
title: [{ text: { content: 'Title' } }]
}
}javascript
// Wrong: Select value as string
properties: {
Status: 'Done'
}
// Correct: Select with proper structure
properties: {
Status: { select: { name: 'Done' } }
}invalid_request
Cause: Request structure is invalid.
javascript
{
"status": 400,
"code": "invalid_request",
"message": "Invalid request: Cannot update a page that was created by an integration."
}Common Causes:
- Attempting unsupported operations
- Wrong endpoint for the action
- Invalid parameter combinations
invalid_request_url
Cause: URL contains invalid characters or format.
javascript
{
"status": 400,
"code": "invalid_request_url",
"message": "Invalid request URL."
}Solutions:
- Encode special characters in URLs
- Verify ID format
- Check for typos in endpoint
invalid_json
Cause: Request body is not valid JSON.
javascript
{
"status": 400,
"code": "invalid_json",
"message": "Error parsing JSON body."
}Solutions:
- Validate JSON syntax
- Check for trailing commas
- Ensure proper encoding
Rate Limit Errors (429)
rate_limited
Cause: Too many requests in a short period.
javascript
{
"status": 429,
"code": "rate_limited",
"message": "Rate limited. Try again later.",
"headers": {
"retry-after": "1"
}
}Solutions:
javascript
const { isNotionClientError, APIErrorCode } = require('@notionhq/client');
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (isNotionClientError(error) && error.code === APIErrorCode.RateLimited) {
const retryAfter = parseInt(error.headers?.['retry-after'] || '1');
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
throw error;
}
}
}Prevention:
- Limit to 3 requests per second
- Implement request queuing
- Use exponential backoff
- Batch operations where possible
Server Errors (500, 502, 503, 504)
internal_server_error
Cause: Notion server encountered an error.
javascript
{
"status": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}Solutions:
- Retry with exponential backoff
- Check Notion status page
- Report if persistent
service_unavailable
Cause: Notion service is temporarily down.
Solutions:
- Wait and retry
- Implement circuit breaker pattern
- Queue requests for later
Conflict Errors (409)
conflict_error
Cause: Resource state conflict, often due to concurrent modifications.
javascript
{
"status": 409,
"code": "conflict_error",
"message": "Cannot perform this operation due to a conflict."
}Solutions:
- Refetch the resource and retry
- Implement optimistic locking
- Add transaction-like logic
Error Handling Best Practices
SDK Error Checking
javascript
const {
Client,
isNotionClientError,
APIErrorCode
} = require('@notionhq/client');
try {
await notion.pages.retrieve({ page_id: 'xxx' });
} catch (error) {
if (isNotionClientError(error)) {
switch (error.code) {
case APIErrorCode.Unauthorized:
// Handle auth error
break;
case APIErrorCode.ObjectNotFound:
// Handle not found
break;
case APIErrorCode.RateLimited:
// Handle rate limit
break;
case APIErrorCode.ValidationError:
// Handle validation
console.log('Validation error:', error.message);
break;
default:
// Handle other Notion errors
console.error('Notion error:', error.code, error.message);
}
} else {
// Handle non-Notion errors (network, etc.)
console.error('Error:', error);
}
}Comprehensive Error Handler
javascript
function handleNotionError(error) {
if (!isNotionClientError(error)) {
return {
type: 'unknown',
message: error.message,
retry: false
};
}
const errorConfig = {
[APIErrorCode.Unauthorized]: {
type: 'auth',
message: 'Check your API key',
retry: false
},
[APIErrorCode.ObjectNotFound]: {
type: 'not_found',
message: 'Resource not found or not accessible',
retry: false
},
[APIErrorCode.RateLimited]: {
type: 'rate_limit',
message: 'Too many requests',
retry: true,
delay: parseInt(error.headers?.['retry-after'] || '1') * 1000
},
[APIErrorCode.ValidationError]: {
type: 'validation',
message: error.message,
retry: false
},
internal_server_error: {
type: 'server',
message: 'Notion server error',
retry: true,
delay: 5000
},
service_unavailable: {
type: 'server',
message: 'Notion temporarily unavailable',
retry: true,
delay: 10000
}
};
return errorConfig[error.code] || {
type: 'unknown',
message: error.message,
retry: false
};
}Quick Troubleshooting
| Error | First Thing to Check |
|---|---|
| 401 Unauthorized | API key format and validity |
| 403 Forbidden | Integration permissions and capabilities |
| 404 Not Found | Page shared with integration? |
| 400 Validation | Property names and types |
| 429 Rate Limited | Add delays between requests |
| 500 Server Error | Retry later, check status.notion.so |