hooksentinel
Errors

idempotency_store_error

hooksentinel error idempotency_store_error — the configured idempotency store (Redis, Prisma, etc.) was unreachable or errored while checking or marking an event.

Summary

FieldValue
Codeidempotency_store_error
HTTP status500
RetryableYes

What caused it

has() or markProcessed() on your configured IdempotencyStore threw — most commonly because the backing store (Redis, your Prisma-managed database) was unreachable, timed out, or returned an error hooksentinel didn't expect. Since hooksentinel can't safely determine whether this event has already been processed, it fails the request rather than guessing — guessing "not a duplicate" risks double-processing, and guessing "is a duplicate" risks silently dropping a real event.

This code is retryable (500) specifically because the failure is about your infrastructure, not the request — the same event redelivered after the store recovers should succeed normally, and the provider's default retry behavior is exactly what you want here.

Common causes:

  • Redis connection issues. Network partition, Redis instance restarted or exhausted its connection limit, wrong REDIS_URL, or TLS misconfiguration against a managed Redis provider.
  • Prisma / database issues. Connection pool exhaustion, database restart, migration not yet applied (the processedWebhookEvent table/model doesn't exist yet in the current environment).
  • Serverless cold-start races. In Lambda or edge environments, a store client created per-invocation may attempt to connect before its underlying network path (e.g. a VPC ENI) is ready.

The fix

Check store connectivity and credentials first — this resolves the large majority of occurrences:

redis-cli -u "$REDIS_URL" ping   # expect PONG
// confirm the Prisma model referenced actually exists and migrations are applied
npx prisma migrate status

Add explicit logging in onError so store outages are visible in your monitoring rather than only showing up as elevated 500s from your webhook endpoint:

onError: async (error, ctx) => {
  if (error.code === 'idempotency_store_error') {
    logger.error('idempotency store unreachable', {
      provider: ctx.provider,
      eventId: ctx.eventId,
      cause: error.cause,
    });
    alerting.page('webhook-idempotency-store-down');
  }
},

For Redis specifically, prefer passing an existing, already-managed ioredis client (with its own reconnection/backoff strategy) over letting redisStore() open a fresh connection per handler instance:

import { redisStore } from '@hooksentinel/core/stores';
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!, {
  maxRetriesPerRequest: 3,
  retryStrategy: (times) => Math.min(times * 200, 2000),
});

redisStore({ client: redis });

If store outages are rare but your application can tolerate occasional double-processing more than it can tolerate rejecting legitimate webhooks during a brief outage, you can catch this specific code in onError and choose to proceed without deduplication as a deliberate fallback — but do this consciously, since it trades correctness for availability.

Last updated on

On this page