Skip to content

Permanent API errors (401, 403, etc.) silently succeed in workpool logs #71

@agucova

Description

@agucova

Problem

When the Resend API returns a permanent error (e.g., 401 Unauthorized due to an invalid API key), the error is recorded in the component's internal email table via markEmailsFailed, but the workpool action returns without throwing (line 559 of src/component/lib.ts):

if (PERMANENT_ERROR_CODES.has(response.status)) {
  // report the error to the user
  await ctx.runMutation(internal.lib.markEmailsFailed, {
    emailIds: args.emails,
    errorMessage: `Resend API error: ${response.status} ${response.statusText} ${await response.text()}`,
  });
  return; // ← returns successfully, workpool counts this as "succeeded"
}

This means:

  1. The workpool reports succeeded: N, failed: 0 — giving the impression emails were sent
  2. No error appears in Convex function logs
  3. The only way to discover the failure is to query the component's internal email status via resend.status(ctx, emailId), which most users won't do proactively

We hit this in production with an expired API key. The Convex logs showed succeeded: 1 for every magic link email attempt, and it took significant debugging to discover that no emails were actually being delivered. The Resend dashboard showed zero emails sent.

Expected behavior

At minimum, permanent API errors should be logged to the Convex function logs (e.g., console.error) so they're visible in the dashboard. Ideally, they should also cause the workpool job to be reported as failed.

Environment

  • @convex-dev/resend: v0.2.3
  • Convex deployment: cloud
  • Error code encountered: 401 (invalid API key)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions