kibinrpc

Error Handling

Structured errors from server to client with KibinError.

On the server

Throw KibinError to send a structured error to the client:

import { KibinError } from '@kibinrpc/server'

throw new KibinError('NOT_FOUND', 'User not found')
throw new KibinError('UNAUTHORIZED', 'Invalid token')
throw new KibinError('BAD_REQUEST', 'Invalid input')

Any other thrown value (a plain Error, a string, etc.) becomes { code: 'INTERNAL_ERROR' }. The original message is not sent to the client to avoid leaking internal details.

HTTP status mapping

Error codeHTTP status
NOT_FOUND404
METHOD_NOT_FOUND404
BAD_REQUEST400
everything else500

On the client

Use isKibinError to distinguish structured server errors from network failures or unexpected exceptions:

import { isKibinError } from '@kibinrpc/client'

try {
  const user = await client.user.getUser('999')
} catch (err) {
  if (isKibinError(err)) {
    // Structured error from the server
    console.log(err.code)    // e.g. 'NOT_FOUND'
    console.log(err.message) // e.g. 'User not found'
  } else {
    // Network error or unexpected exception
    console.error(err)
  }
}

isKibinError is a TypeScript type guard — inside the if block, err is typed as KibinError.

KibinError type

import type { KibinError } from '@kibinrpc/client'

err.code     // string — the error code from the server
err.message  // string — the human-readable message

Batched calls

In a batched request, each item has its own error or data. Failed items throw individually — a partial failure does not reject the whole Promise.all:

try {
  const [users, post] = await Promise.all([
    client.user.listUsers(),       // succeeds
    client.post.getPost('missing'), // throws NOT_FOUND
  ])
} catch (err) {
  if (isKibinError(err)) {
    console.log(err.code) // 'NOT_FOUND'
  }
}

The HTTP response for that batch is 207 Multi-Status. The client unpacks each item and resolves or rejects each promise independently.

Error interceptor

Handle errors centrally with the client error interceptor:

const client = createKibinClient<AppRouter>({
  baseUrl: '/api/rpc',
  interceptors: {
    error({ error }) {
      if (error.code === 'UNAUTHORIZED') {
        window.location.href = '/login'
        return // suppress — redirect handles it
      }
      throw error // rethrow everything else
    },
  },
})

Returning from the error interceptor (instead of throwing) resolves the promise with undefined. Rethrowing propagates the error to the caller.

On this page