payloadcms/payload · error · APIError

Failed to create new '${syncConfig.stripeResourceType}' reso

Error message

Failed to create new '${syncConfig.stripeResourceType}' resource in Stripe: ${msg}

What it means

Thrown by the Stripe plugin's `createNewInStripe` before-validate hook during a Payload `create` operation when `stripe.[stripeResourceType].create(syncedFields)` rejects. The original Stripe SDK error message is interpolated into the APIError, so the cause (invalid params, auth, rate limit) is preserved.

Source

Thrown at packages/plugin-stripe/src/hooks/createNewInStripe.ts:133

            // NOTE: Typed as "any" because the "create" method is not standard across all Stripe resources
            const stripeResource = await stripe?.[syncConfig.stripeResourceType]?.create(
              // @ts-expect-error
              syncedFields,
            )

            if (logs) {
              payload.logger.info(
                `✅ Successfully created new '${syncConfig.stripeResourceType}' resource in Stripe with ID: '${stripeResource.id}'.`,
              )
            }

            dataRef.stripeID = stripeResource.id

            // IMPORTANT: this is to prevent sync in the "afterChange" hook
            dataRef.skipSync = true
          } catch (error: unknown) {
            const msg = error instanceof Error ? error.message : error
            throw new APIError(
              `Failed to create new '${syncConfig.stripeResourceType}' resource in Stripe: ${msg}`,
            )
          }
        }
      }
    }
  }

  return dataRef
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Read the `${msg}` suffix — it is the Stripe SDK error (e.g. 'No such customer', 'Invalid API key')
  2. Verify `stripeSecretKey` in the plugin config matches the environment (test vs live)
  3. Check the `syncConfig.fields` mapping produces a valid object for the `stripeResourceType.create` signature
  4. Confirm `stripeResourceType` is a real Stripe resource that supports `.create` (e.g. `customers`, `products`, not `balance`)

Example fix

// before — wrong/missing secret key
stripePlugin({ stripeSecretKey: process.env.STRIPE_KEY /* undefined */ })
// after
stripePlugin({ stripeSecretKey: process.env.STRIPE_SECRET_KEY! })
Defensive patterns

Strategy: try-catch

Validate before calling

// Sanity-check the secret key and resource type at boot
if (!process.env.STRIPE_SECRET_KEY) throw new Error('STRIPE_SECRET_KEY missing')
if (typeof (stripe as any)[syncConfig.stripeResourceType]?.create !== 'function')
  throw new Error(`${syncConfig.stripeResourceType} has no .create method`)

Type guard

import { APIError } from 'payload'
function isStripeCreateError(e: unknown): e is APIError {
  return e instanceof APIError && /^Failed to create new '.*' resource in Stripe:/.test(e.message)
}

Try / catch

import { APIError } from 'payload'
try {
  await payload.create({ collection: 'users', data })
} catch (e) {
  if (e instanceof APIError && /Failed to create new .* resource in Stripe/.test(e.message)) {
    // e.message suffix is the Stripe SDK error; route to retry / user-facing message
  }
  throw e
}

Prevention

When it happens

Trigger: Creating a Payload document in a Stripe-synced collection whose `syncedFields` are rejected by Stripe (missing required field, invalid email, duplicate customer); `stripeSecretKey` is missing/wrong so the SDK throws an auth error; `stripeResourceType` does not have a `.create` method.

Common situations: `stripeSecretKey` not set in the plugin config or set to a test key in production; field mapping in `syncConfig.fields` produces an invalid Stripe object; Stripe API version drift between the SDK pin (`2022-08-01`) and required params; rate limiting from bulk imports.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/cd9c2be9a379c012. Report an issue: GitHub.