{"record":{"id":"051adc9e963ac266","repo":"koala73/worldmonitor","slug":"invalid-prefix","errorCode":"INVALID_PREFIX","errorMessage":"INVALID_PREFIX","messagePattern":"INVALID_PREFIX","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/apiKeys.ts","lineNumber":81,"sourceCode":"    ) {\n      throw new ConvexError(\"API_ACCESS_REQUIRED\");\n    }\n\n    const scopes = normalizeCompanyMonitoringScopes(args.scopes);\n    // Issuing a scoped key is a first-use entry point, so it provisions the\n    // root. Requesting no scopes must stay entirely off Company Monitoring.\n    const companyMonitoringAccount = scopes\n      ? await ensureActiveAccount(ctx, userId, entitlement)\n      : null;\n    if (scopes && !companyMonitoringAccount) {\n      throw new ConvexError(\"COMPANY_MONITORING_ACCESS_DENIED\");\n    }\n\n    if (!args.name.trim()) {\n      throw new ConvexError(\"INVALID_NAME\");\n    }\n    if (!/^wm_[a-f0-9]{5}$/.test(args.keyPrefix)) {\n      throw new ConvexError(\"INVALID_PREFIX\");\n    }\n    if (!/^[a-f0-9]{64}$/.test(args.keyHash)) {\n      throw new ConvexError(\"INVALID_HASH\");\n    }\n\n    // Enforce per-user key limit (count only non-revoked keys).\n    //\n    // API keys intentionally reject at the cap instead of silently rotating a\n    // valid key. If a prior race left too many active rows, converge by\n    // revoking enough oldest overflow rows to make room for this create.\n    const existing = await ctx.db\n      .query(\"userApiKeys\")\n      .withIndex(\"by_userId\", (q) => q.eq(\"userId\", userId))\n      .collect();\n    const active = existing.filter((k) => !k.revokedAt);\n    let activeCount = active.length;\n    if (active.length > MAX_KEYS_PER_USER) {\n      active.sort((a, b) => a.createdAt - b.createdAt);","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/apiKeys.ts#L63-L99","documentation":"Input validation on the API-key create mutation: args.keyPrefix does not match the required shape ^wm_[a-f0-9]{5}$ — a 'wm_' prefix followed by exactly five lowercase hex characters. The prefix is generated client-side from the raw key and stored for display, so a malformed prefix means the client generated the key incorrectly or the value was tampered with.","triggerScenarios":"Thrown at convex/apiKeys.ts:81 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Regenerate the API key client-side using the correct 'wm_' + 5 lowercase hex chars prefix format","Validate the prefix against ^wm_[a-f0-9]{5}$ before calling the create mutation"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}