GuidesError handling

Error handling

This guide covers the errors you may encounter when using FlushKit and the recommended way to handle each one.

SDK errors

StateError — double init

Calling FlushbarRemote.init a second time without first calling dispose throws a StateError:

code
StateError: FlushbarRemote is already initialised. Call dispose() first.

Fix: Ensure init is only called once. If you need to swap the API key at runtime, call dispose first:

dart
FlushbarRemote.dispose();
FlushbarRemote.init(apiKey: newKey, context: context);

Stream errors — connection failures

Network errors surface on the onError callback of the events stream. The SDK will retry automatically using exponential backoff — you do not need to reconnect manually.

dart
FlushbarRemote.events.stream.listen(
  (event) { /* ... */ },
  onError: (error) {
    // Log the error, but don't try to reconnect — the SDK handles it
    analytics.recordError(error);
  },
);

API errors

All REST API errors follow the same envelope:

json
{ "code": "ERROR_CODE", "message": "Human-readable description." }

UNAUTHORIZED (401)

Your API key is missing, malformed, or has been revoked.

json
{ "code": "UNAUTHORIZED", "message": "Invalid or missing API key." }

Fix: Check that you are including Authorization: Bearer fk_live_... in your request headers. If you recently rotated your key, update your server environment variable.

VALIDATION_ERROR (422)

A required field is missing or a value is out of range.

json
{ "code": "VALIDATION_ERROR", "message": "message is required." }

Fix: Check the POST /v1/notify reference for required fields and allowed values.

EVENT_LIMIT_REACHED (429)

Your project has exhausted its monthly notification quota.

json
{
  "code": "EVENT_LIMIT_REACHED",
  "limit": 1000,
  "used": 1000
}

Fix: Upgrade your plan from the billing page, or wait until the quota resets at the start of the next billing period.

PROJECT_LIMIT_REACHED (403)

Your plan only allows a limited number of projects.

json
{
  "code": "PROJECT_LIMIT_REACHED",
  "limit": 1,
  "current": 1
}

Fix: Upgrade to Starter or Growth plan for unlimited projects.

Handling errors in server code

A robust server-side notify function should handle the two quota errors gracefully:

ts
async function sendPushNotification(message: string) {
  const res = await fetch('https://api.flushkit.dev/v1/notify', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FLUSHKIT_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ message }),
  })

  if (res.status === 429) {
    const body = await res.json()
    if (body.code === 'EVENT_LIMIT_REACHED') {
      // Soft failure — log and continue, don't crash
      console.warn(`FlushKit quota reached: ${body.used}/${body.limit}`)
      return
    }
  }

  if (!res.ok) {
    throw new Error(`FlushKit error ${res.status}`)
  }
}

Treat EVENT_LIMIT_REACHED as a soft failure in production. Crashing or blocking your main flow because a push notification couldn't be sent is usually worse than silently skipping it.

Notification not received?

If a notification was sent but not received, check these in order:

1. Was the app open when you sent it? FlushKit queues notifications sent while the app is closed or offline and replays them automatically on the next SSE connection.

2. Check the delivered count In the FlushKit dashboard notification log, check the "Clients" column. If it shows 0 clients, no devices were connected when the notification was sent.

3. Check the SSE connection In your Flutter app, add a listener to verify the connection is active:

dart
FlushbarRemote.events.listen(
  (event) => debugPrint('received: ${event.message}'),
  onError: (e) => debugPrint('connection error: $e'),
)

4. API key mismatch Verify the API key in your Flutter app matches the one shown in the FlushKit dashboard for your project.