medusajs/medusa · error · MedusaError

You must provide an api key id field when revoking a key.

Error message

You must provide an api key id field when revoking a key.

What it means

Thrown by validateRevokeApiKeys_ when revoking API keys: every entry in the revoke payload must include an id (and subsequently revoked_by). It guards the batch revoke input shape before touching the database.

Source

Thrown at packages/modules/api-key/src/services/api-key-module-service.ts:539

            id: apiKey.id,
            ...data,
          } as T)
      )
    }

    return normalizedInput
  }

  protected async validateRevokeApiKeys_(
    data: RevokeApiKeyInput[],
    sharedContext: Context = {}
  ): Promise<void> {
    if (!data.length) {
      return
    }

    if (data.some((k) => !k.id)) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `You must provide an api key id field when revoking a key.`
      )
    }

    if (data.some((k) => !k.revoked_by)) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `You must provide a revoked_by field when revoking a key.`
      )
    }

    const revokedApiKeys = await this.apiKeyService_.list(
      {
        id: data.map((k) => k.id),
        type: ApiKeyType.SECRET,
        revoked_at: { $lt: new Date() },
      },

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Ensure every element of the revoke data array has a non-empty id string
  2. When revoking from admin routes, pass the :id route param into the payload explicitly
  3. Add payload validation before calling the service to fail early with clearer errors

Example fix

// before
await service.revokeApiKeys({
  data: [{ revoked_by: "user_1" }],
})

// after
await service.revokeApiKeys({
  data: [{ id: "apk_123", revoked_by: "user_1" }],
})
Defensive patterns

Strategy: validation

Validate before calling

const payload = data.filter(d => typeof d.id === 'string' && d.id.length > 0)
if (payload.length !== data.length) {
  throw new Error('every revoke entry requires an id')
}
await service.revokeApiKeys({ data: payload })

Type guard

const isRevokeInput = (
  d: unknown
): d is { id: string; revoked_by: string } =>
  typeof d === 'object' && d !== null &&
  typeof (d as any).id === 'string' && (d as any).id.length > 0 &&
  typeof (d as any).revoked_by === 'string'

Try / catch

try {
  await service.revokeApiKeys({ data })
} catch (e) {
  if (e.type === 'invalid_data' && /api key id field/.test(e.message)) {
    // filter/rebuild payload entries missing id and retry
  }
  throw e
}

Prevention

When it happens

Trigger: Calling apiKeyModuleService.revokeApiKeys with a payload entry missing id — e.g. {data: [{revoked_by: 'x'}]} — or via POST /admin/api-keys/:id/revoke flows where the id isn't propagated into the payload.

Common situations: Custom scripts building revoke payloads dynamically where id can be undefined (bad variable name, optional chaining result); frontend sending an empty id field.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/c257d574819b7517. Report an issue: GitHub.