{"record":{"id":"6b905d0e18213ecf","repo":"medusajs/medusa","slug":"you-must-provide-a-revoked-by-field-when-revoking","errorCode":null,"errorMessage":"You must provide a revoked_by field when revoking a key.","messagePattern":"You must provide a revoked_by field when revoking a key\\.","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/modules/api-key/src/services/api-key-module-service.ts","lineNumber":546,"sourceCode":"  }\n\n  protected async validateRevokeApiKeys_(\n    data: RevokeApiKeyInput[],\n    sharedContext: Context = {}\n  ): Promise<void> {\n    if (!data.length) {\n      return\n    }\n\n    if (data.some((k) => !k.id)) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        `You must provide an api key id field when revoking a key.`\n      )\n    }\n\n    if (data.some((k) => !k.revoked_by)) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        `You must provide a revoked_by field when revoking a key.`\n      )\n    }\n\n    const revokedApiKeys = await this.apiKeyService_.list(\n      {\n        id: data.map((k) => k.id),\n        type: ApiKeyType.SECRET,\n        revoked_at: { $lt: new Date() },\n      },\n      {},\n      sharedContext\n    )\n\n    if (revokedApiKeys.length) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/api-key/src/services/api-key-module-service.ts#L528-L564","documentation":"Thrown by the API Key module's revoke validation when one or more keys in the revoke batch lack a revoked_by value. The module records who revoked each key for audit purposes, so the field is mandatory on every revoke call.","triggerScenarios":"Calling revokeApiKeys / revoke workflow with input objects that omit revoked_by, e.g. revoke([{ id: 'apk_...' }]) — any entry in the array missing revoked_by triggers it.","commonSituations":"Scripts or admin customizations that copy the create/update payload shape for revoke; passing only ids; forgetting that revoked_by is per-item, not a top-level argument.","solutions":["Add revoked_by (typically the logged-in admin user id) to every item: revoke([{ id, revoked_by: authUserId }])","If calling from a route, pass req.auth_context.user_id as revoked_by","Check the zod/schema validation upstream so the field is required before reaching the module"],"exampleFix":"// before\nawait apiKeyModuleService.revoke([{ id: apiKeyId }])\n// after\nawait apiKeyModuleService.revoke([\n  { id: apiKeyId, revoked_by: loggedInUserId },\n])","handlingStrategy":"validation","validationCode":"const input = [{ id, revoked_by }]\nif (input.some((k) => !k.revoked_by)) {\n  throw new Error('revoked_by is required for every key')\n}\nawait apiKeyModuleService.revoke(input)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass the acting admin user id as revoked_by on every revoke call","Validate the revoke payload shape client-side before hitting the API"],"tags":["api-key","validation","revocation"],"backgroundTag":"missing-required-field","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}