nextauthjs/next-auth · error · TypeError

Missing Loops API Key or TransactionalId

Error message

Missing Loops API Key or TransactionalId

What it means

The Loops provider throws a TypeError in sendVerificationRequest when either provider.apiKey or provider.transactionalId is falsy. Both are mandatory to call Loops' transactional email API, so the provider validates them up front before making any network request. This is a configuration error, not a runtime/network failure.

Source

Thrown at packages/core/src/providers/loops.ts:57

 * })
 * ```
 *
 * @typedef LoopsUserConfig
 */

export default function Loops(config: LoopsUserConfig): LoopsConfig {
  return {
    id: "loops",
    apiKey: "",
    type: "email",
    name: "Loops",
    from: "Auth.js <no-reply@authjs.dev>",
    maxAge: 24 * 60 * 60,
    transactionalId: config.transactionalId || "",
    async sendVerificationRequest(params: Params) {
      const { identifier: to, provider, url } = params
      if (!provider.apiKey || !provider.transactionalId)
        throw new TypeError("Missing Loops API Key or TransactionalId")

      const res = await fetch("https://app.loops.so/api/v1/transactional", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${provider.apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          transactionalId: provider.transactionalId,
          email: to,
          dataVariables: {
            url: url,
          },
        }),
      })
      if (!res.ok) {
        throw new Error("Loops Send Error: " + JSON.stringify(await res.json()))
      }

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Set the API key: pass apiKey: process.env.LOOPS_API_KEY and ensure the env var exists where the app runs
  2. Create a transactional email in Loops (with a {{url}} data variable) and pass its transactionalId to the provider
  3. Double-check provider option key names (apiKey, transactionalId) match the provider's expected shape
  4. Add a startup assertion/log that both fields are present before accepting traffic

Example fix

// before
Loops({ transactionalId: config.transactionalId || "" }) // empty string -> throw
// after
Loops({
  apiKey: process.env.LOOPS_API_KEY,
  transactionalId: process.env.LOOPS_TRANSACTIONAL_ID, // set in env
})
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.LOOPS_API_KEY) throw new Error('LOOPS_API_KEY is required')
if (!process.env.LOOPS_TRANSACTIONAL_ID) throw new Error('LOOPS_TRANSACTIONAL_ID is required')

Type guard

function isLoopsConfig(c: { apiKey?: string; transactionalId?: string }): c is { apiKey: string; transactionalId: string } {
  return typeof c.apiKey === 'string' && c.apiKey.length > 0 && typeof c.transactionalId === 'string' && c.transactionalId.length > 0
}

Try / catch

try {
  await sendVerificationRequest(params)
} catch (e) {
  if (e instanceof TypeError && e.message.includes('Missing Loops API Key')) {
    console.error('Loops provider misconfigured: set apiKey and transactionalId')
  }
}

Prevention

When it happens

Trigger: Calling the Loops email provider's sendVerificationRequest with a config missing apiKey (e.g. process.env.LOOPS_API_KEY unset) or transactionalId (not passed and not defaulted) — the guard `if (!provider.apiKey || !provider.transactionalId)` fires immediately.

Common situations: Forgetting to set LOOPS_API_KEY in the deployment environment; creating the provider without a transactionalId because the developer hasn't created the transactional email template in Loops yet; typos in config keys so the fields land undefined.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/0fb6e04df58234ac. Report an issue: GitHub.