{"record":{"id":"7c9d4dada3fd82d3","repo":"koala73/worldmonitor","slug":"invalid-name-7c9d4d","errorCode":"INVALID_NAME","errorMessage":"INVALID_NAME","messagePattern":"INVALID_NAME","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/embedKeys.ts","lineNumber":86,"sourceCode":"      .first();\n    // Merge before gating, unlike createApiKey. `apiAccess` has existed since\n    // the first entitlement row, so reading it raw is safe; `embedAccess` is\n    // new, so EVERY row written before this deploy omits it and the predicate\n    // is fail-closed on `undefined`. Gating on the stored value alone would\n    // lock every existing paid subscriber out of the feature until a Dodo\n    // billing event happened to rewrite their row.\n    const merged = entitlement\n      ? {\n          features: mergeEntitlementFeatures(entitlement.planKey, entitlement.features),\n          validUntil: entitlement.validUntil,\n        }\n      : null;\n    if (!hasAccountEmbedAccess(identity?.plan, merged, Date.now())) {\n      throw new ConvexError(\"EMBED_ACCESS_REQUIRED\");\n    }\n\n    if (!args.name.trim()) {\n      throw new ConvexError(\"INVALID_NAME\");\n    }\n    if (!/^wme_[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    const allowedOrigins = normalizeAllowedOrigins(args.allowedOrigins);\n\n    const active = await ctx.db\n      .query(\"embedKeys\")\n      .withIndex(\"by_userId_revokedAt\", (q) =>\n        q.eq(\"userId\", userId).eq(\"revokedAt\", undefined),\n      )\n      .collect();\n    if (active.length >= MAX_EMBED_KEYS_PER_USER) {\n      throw new ConvexError(\"KEY_LIMIT_REACHED\");\n    }","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/embedKeys.ts#L68-L104","documentation":"createEmbedKey requires a non-blank human-readable key name: `args.name.trim()` must be truthy. ConvexError(\"INVALID_NAME\") is thrown for an empty string or a string of only whitespace. This runs after the entitlement gate and before prefix/hash validation.","triggerScenarios":"Calling createEmbedKey with name: \"\" or name: \"   \" (whitespace-only), or with the name field omitted/undefined when the client bypasses the v.string() arg validation with a hand-built request.","commonSituations":"A dashboard form submitted without the display-name field filled in; form state initialized to \"\" and never bound; trimming happening only on the server so a whitespace-pasted value reaches the mutation; automated scripts sending minimal payloads.","solutions":["Pass a non-empty name with at least one non-whitespace character, e.g. \"Marketing site widget\".","Validate in the form/UI before invoking the mutation: require name.trim().length > 0.","If the value comes from user input, trim it client-side before sending so stored and validated forms agree."],"exampleFix":"// before\nawait api.embedKeys.createEmbedKey({ name: \"  \", keyPrefix, keyHash });\n// after\nconst name = formValue.trim();\nif (!name) throw new Error(\"Key name is required\");\nawait api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash });","handlingStrategy":"validation","validationCode":"const name = rawName.trim();\nif (!name) throw new Error(\"Embed key name is required\");","typeGuard":"function hasNonEmptyName(name: unknown): name is string {\n  return typeof name === \"string\" && name.trim().length > 0;\n}","tryCatchPattern":"try {\n  await api.embedKeys.createEmbedKey({ ...args, name: args.name.trim() });\n} catch (e) {\n  if (e instanceof ConvexError && e.data === \"INVALID_NAME\") {\n    // mark the name field required in the form\n  } else throw e;\n}","preventionTips":["Mark the name input required and block submit when name.trim() is empty","Trim client-side before sending so validation and stored value match","Never initialize the form field to whitespace-only defaults"],"tags":["convex","validation","input-validation","embed-keys"],"backgroundTag":"empty-required-field","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}