{"record":{"id":"c257d574819b7517","repo":"medusajs/medusa","slug":"you-must-provide-an-api-key-id-field-when-revoking","errorCode":null,"errorMessage":"You must provide an api key id field when revoking a key.","messagePattern":"You must provide an api key id 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":539,"sourceCode":"            id: apiKey.id,\n            ...data,\n          } as T)\n      )\n    }\n\n    return normalizedInput\n  }\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      },","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/api-key/src/services/api-key-module-service.ts#L521-L557","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom scripts building revoke payloads dynamically where id can be undefined (bad variable name, optional chaining result); frontend sending an empty id field.","solutions":["Ensure every element of the revoke data array has a non-empty id string","When revoking from admin routes, pass the :id route param into the payload explicitly","Add payload validation before calling the service to fail early with clearer errors"],"exampleFix":"// before\nawait service.revokeApiKeys({\n  data: [{ revoked_by: \"user_1\" }],\n})\n\n// after\nawait service.revokeApiKeys({\n  data: [{ id: \"apk_123\", revoked_by: \"user_1\" }],\n})","handlingStrategy":"validation","validationCode":"const payload = data.filter(d => typeof d.id === 'string' && d.id.length > 0)\nif (payload.length !== data.length) {\n  throw new Error('every revoke entry requires an id')\n}\nawait service.revokeApiKeys({ data: payload })","typeGuard":"const isRevokeInput = (\n  d: unknown\n): d is { id: string; revoked_by: string } =>\n  typeof d === 'object' && d !== null &&\n  typeof (d as any).id === 'string' && (d as any).id.length > 0 &&\n  typeof (d as any).revoked_by === 'string'","tryCatchPattern":"try {\n  await service.revokeApiKeys({ data })\n} catch (e) {\n  if (e.type === 'invalid_data' && /api key id field/.test(e.message)) {\n    // filter/rebuild payload entries missing id and retry\n  }\n  throw e\n}","preventionTips":["Type the revoke payload explicitly and validate ids before calling the service","When building revoke data from route params, assert the param is non-empty"],"tags":["api-key","validation","revoke","input-validation"],"backgroundTag":"missing-required-field","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}