API Status Get API Key

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 errors array 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_EXCEEDED with exponential backoff. See Rate Limits for details on limits and retry headers.

Next steps