Error Handling
The Buffer API uses two categories of errors. Here's how they work and how to handle them in your code.
Two types of errors
| Type | Where | When | HTTP Status |
|---|---|---|---|
| Typed mutation errors | In the response data |
User-fixable problems (validation, limits) | 200 |
| Non-recoverable errors | In the errors array |
System problems (auth, not found, server) | 200 |
GraphQL always returns HTTP 200. Check the response body to determine success or failure.
Typed mutation errors
Mutations return a union type that includes both the success case and possible error cases. This lets you handle each error type differently in your code.
Basic pattern
Always include ... on MutationError in every mutation:
mutation {
createPost(input: {
text: "Hello world",
channelId: "your_channel_id",
schedulingType: automatic,
mode: addToQueue
}) {
... on PostActionSuccess {
post {
id
text
}
}
... on MutationError {
message
}
}
}
If the mutation succeeds, you get PostActionSuccess. If it fails, you get a MutationError with a human-readable message.
Handling specific error types
Some mutations return specific error types with additional data. You can match on these for more precise handling:
mutation {
createPost(input: { ... }) {
... on PostActionSuccess {
post { id }
}
... on LimitReachedError {
message
}
... on InvalidInputError {
message
}
... on MutationError {
message
}
}
}
The ... on MutationError at the end acts as a catch-all. Because all error types implement the MutationError interface, any error type you don't explicitly handle will still return a message.
InvalidInputError
When input validation fails, you get an error message:
{
"data": {
"createPost": {
"message": "Text is required"
}
}
}
Future-proofing with VoidMutationError
Some mutations include a VoidMutationError in their union. The API never explicitly returns this type, but it ensures that if new error types are added later, your ... on MutationError catch-all will still receive the message - no code changes needed.
This is why you should always include ... on MutationError in every mutation.
Non-recoverable errors
System-level errors appear in the GraphQL errors array. These indicate problems you typically can't fix by changing your input.
Error codes
| Code | Meaning | What to do |
|---|---|---|
UNAUTHORIZED |
Missing or invalid API key | Check your Authorization header and API key |
FORBIDDEN |
Valid key, but no permission | Verify you're accessing resources in your own account |
NOT_FOUND |
Resource doesn't exist | Check the ID you're using is correct |
UNEXPECTED |
Server error | Retry after a short delay; contact support if persistent |
RATE_LIMIT_EXCEEDED |
Too many requests | Wait and retry; see Rate Limits |
Example error response
{
"data": null,
"errors": [
{
"message": "Not authorized",
"extensions": {
"code": "UNAUTHORIZED"
}
}
]
}
Error handling snippet
Here's a reusable pattern for handling both error types:
async function bufferRequest(query, variables = {}) {
const response = await fetch('https://api.buffer.com', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.BUFFER_API_KEY}`,
},
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
// Check for non-recoverable errors
if (result.errors) {
const error = result.errors[0];
const code = error.extensions?.code;
if (code === 'RATE_LIMIT_EXCEEDED') {
// Wait and retry
throw new Error('Rate limited, try again later');
}
throw new Error(`API error (${code}): ${error.message}`);
}
return result.data;
}
For mutations, check the response type:
const data = await bufferRequest(`
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
... on PostActionSuccess { post { id } }
... on MutationError { message }
}
}
`, { input: postInput });
if (data.createPost.post) {
// Success
console.log('Created post:', data.createPost.post.id);
} else if (data.createPost.message) {
// Typed error
console.error('Mutation error:', data.createPost.message);
}
Best practices
- Always include
... on MutationError { message }in every mutation. This catches current and future error types. - Check the
errorsarray on every response. Even successful mutations can include warnings. - Don't display raw error messages to end users. Use the error type to decide what to show.
- Log the full error response for debugging. Include the query, variables, and complete response.
- Handle
RATE_LIMIT_EXCEEDEDwith exponential backoff. See Rate Limits for details on limits and retry headers.
Next steps
- API Standards: full details on the API's error design philosophy
- Rate Limits: understand request limits and throttling
- Your First Post: see error handling in action