Skip to content

fix(outbox): fail fast when Prisma client lacks eventOutbox model (#1380) - #1495

Merged
Junirezz merged 3 commits into
Junirezz:mainfrom
solaawojobi00-bit:fix/issue-1380-event-outbox-fail-fast
Oct 3, 2026
Merged

Junirezz merged 3 commits into
Junirezz:mainfrom
solaawojobi00-bit:fix/issue-1380-event-outbox-fail-fast

Conversation

@solaawojobi00-bit

Copy link
Copy Markdown
Contributor

Fail fast when the Prisma client is missing the eventOutbox model

Problem

EventOutboxService.writeEvent calls prisma.eventOutbox.create(...) directly. If the Prisma client has no eventOutbox delegate (for example, a stale generated client or an execution context without the model), the call throws TypeError: Cannot read properties of undefined (reading 'create'). None of the call sites let that error through:

  • vaultEndpoints.ts calls writeEvent in the background (void … .catch(log)), so the failure is only logged and the event is silently lost.
  • start() wraps every processOutbox() poll in .catch(log), and processOutbox has its own try/catch, so the background processor keeps logging the same error on every cycle.
  • The server boots normally, so the misconfiguration isn't obvious.
Scenario Before After
Server boot with client missing eventOutbox Boots; errors logged each poll start() throws at boot with a clear message
writeEvent with missing model TypeError: Cannot read properties of undefined, swallowed by caller Rejects with a clear missing the eventOutbox model error
replayOnStartup with missing model TypeError, logged Rejects with a clear error
Healthy client Works Unchanged

Solution

Add a single guard, assertEventOutboxModelAvailable(), and call it at the service's entry points. start() is called synchronously and without error handling in index.ts, so the guard there makes startup fail immediately (the fail-fast during initialization the issue asks for). The guards in writeEvent and replayOnStartup replace the TypeError with a message that says how to fix it.

Changes

backend/src/eventOutbox.ts

export function assertEventOutboxModelAvailable(client: unknown = prisma): void {
  const delegate = (client as { eventOutbox?: { create?: unknown } } | null | undefined)?.eventOutbox;
  if (!delegate || typeof delegate.create !== 'function') {
    throw new Error(
      'Prisma client is missing the eventOutbox model. Run `prisma generate` and ensure the EventOutbox migration is applied.',
    );
  }
}
  • The guard is called first in start(), before isRunning is set, so a failed start leaves the processor inactive and no timers are created.
  • It is also called first in writeEvent() and replayOnStartup().
  • The client parameter defaults to the shared prisma instance, so the function can be unit-tested without mocking modules.

backend/src/__tests__/eventOutboxModelGuard.test.ts (new)

This file uses jest.isolateModules + jest.doMock('../prisma') to load the service against a client that has no eventOutbox model. The existing eventOutbox.test.ts suite still runs against the real database.

Regression Tests

Acceptance criterion Test
Missing model is detected assertEventOutboxModelAvailable throws for undefined / null / {} / { eventOutbox: {} }
No false positives passes when eventOutbox.create is a function
Fail fast during initialization start() throws synchronously and isActive stays false
Error is surfaced, not an opaque TypeError writeEvent rejects with missing the eventOutbox model
Startup replay surfaces the error replayOnStartup rejects with missing the eventOutbox model
Existing behaviour unchanged full existing eventOutbox.test.ts suite passes

Testing

$ npx jest --runInBand src/__tests__/eventOutboxModelGuard.test.ts src/__tests__/eventOutbox.test.ts

Test Suites: 2 passed, 2 total
Tests:       30 passed, 30 total
Snapshots:   0 total

npx eslint src/eventOutbox.ts src/__tests__/eventOutboxModelGuard.test.ts reports no problems, and tsc --noEmit reports no errors in the changed files.

Notes for Reviewers

  • Scope: this change is limited to the outbox service. The background .catch(log) pattern in vaultEndpoints.ts is unchanged: with the boot-time guard, a server that is running is known to have the model, so request handling doesn't need to change.
  • Risk: low. With a correctly generated client the only added work is a property check per call. The only new behaviour is that boot now fails on a misconfigured client, which is what the issue asks for.
  • Rollback: a clean git revert. There is no schema or state change.

Closes #1380

@drips-wave

drips-wave Bot commented Sep 29, 2026

Copy link
Copy Markdown

@solaawojobi00-bit Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Junirezz
Junirezz merged commit ff64a3d into Junirezz:main Oct 3, 2026
11 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eventOutbox writeEvent silently swallows undefined-model reference errors

2 participants