Good Webhooks

Errors

Expected operation errors and failures recorded in delivery history.

Every expected failure throws a WebhookError with a stable code.

import { WebhookError } from 'good-webhooks'
import type { WebhookErrorCode } from 'good-webhooks'

The receiver entry point good-webhooks/verify also exports the class and code type.

WebhookError

class WebhookError extends Error {
  readonly name: 'WebhookError'
  readonly code: WebhookErrorCode
  constructor(code: WebhookErrorCode, message: string, options?: ErrorOptions)
}
import { WebhookError } from 'good-webhooks'
import { webhooks } from './webhooks.js'

try {
  await webhooks.publish({
    type: 'invoice.paid',
    data: { invoiceId: 'inv_123', amount: 4200 },
    idempotencyKey: 'invoice.paid:inv_123',
  })
} catch (error) {
  if (error instanceof WebhookError && error.code === 'IDEMPOTENCY_CONFLICT') {
    console.error('Idempotency key reused for a different payload:', error.message)
  }
  throw error
}

WebhookError extends Error. It has name: 'WebhookError', a stable code, a descriptive message, and an optional cause.

Codes

CodeMeaningThrown by
INVALID_CONFIGInvalid constructor, encryption, migration, or receiver-validator configuration.Constructors, getPostgresMigration, parseWebhook, secret decryption
INVALID_INPUTInvalid operation arguments or producer payload.Most operations
UNSAFE_URLDestination fails URL or resolved-address validation.Endpoint create, update, and resume
NOT_FOUNDRecord or owner is absent in the selected scope.Endpoint and delivery lookups
INVALID_STATEOperation conflicts with lifecycle, limits, retention, or concurrent state.Endpoint lifecycle, replay, worker overlap
IDEMPOTENCY_CONFLICTAn accepted key identifies different event content.publish
REPLAY_IN_PROGRESSAn original delivery already has a pending or in-flight replay.replay
SCHEMA_MISMATCHExpected storage is missing or incompatible.check(), Better Auth record reads
SIGNATURE_INVALIDWebhook headers or signature are invalid.verifyWebhook, parseWebhook
SIGNATURE_EXPIREDWebhook timestamp falls outside the configured tolerance.verifyWebhook, parseWebhook
PAYLOAD_INVALIDAn authenticated webhook body fails envelope or receiver-payload validation.parseWebhook
TRANSACTION_REQUIREDA supplied publication client has no active PostgreSQL transaction.publish with options.transaction

Each reference page lists the exact conditions for its operations.

Failures outside WebhookError

Database errors, provider lookup failures, and unexpected validator failures can propagate as other error types. Code that handles WebhookError still needs a path for unexpected failures.

An HTTP recipient failure normally becomes a delivery and attempt record. It does not reject publish, because publication queues work without sending it. Worker internal failures can reject runOnce with an AggregateError. See retries and replay.

The Better Auth plugin maps expected management errors to HTTP responses. That mapping does not change the direct provider API.

On this page