koala73/worldmonitor · error · ConvexError

INVALID_NAME

INVALID_NAME

Error message

INVALID_NAME

What it means

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.

Solutions

  1. Pass a non-empty name with at least one non-whitespace character, e.g. "Marketing site widget".
  2. Validate in the form/UI before invoking the mutation: require name.trim().length > 0.
  3. If the value comes from user input, trim it client-side before sending so stored and validated forms agree.

Example fix

// before
await api.embedKeys.createEmbedKey({ name: "  ", keyPrefix, keyHash });
// after
const name = formValue.trim();
if (!name) throw new Error("Key name is required");
await api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash });
Defensive patterns

Strategy: validation

Validate before calling

const name = rawName.trim();
if (!name) throw new Error("Embed key name is required");

Type guard

function hasNonEmptyName(name: unknown): name is string {
  return typeof name === "string" && name.trim().length > 0;
}

Try / catch

try {
  await api.embedKeys.createEmbedKey({ ...args, name: args.name.trim() });
} catch (e) {
  if (e instanceof ConvexError && e.data === "INVALID_NAME") {
    // mark the name field required in the form
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/7c9d4dada3fd82d3. Report an issue: GitHub.

Appendix: source

Thrown at convex/embedKeys.ts:86

      .first();
    // Merge before gating, unlike createApiKey. `apiAccess` has existed since
    // the first entitlement row, so reading it raw is safe; `embedAccess` is
    // new, so EVERY row written before this deploy omits it and the predicate
    // is fail-closed on `undefined`. Gating on the stored value alone would
    // lock every existing paid subscriber out of the feature until a Dodo
    // billing event happened to rewrite their row.
    const merged = entitlement
      ? {
          features: mergeEntitlementFeatures(entitlement.planKey, entitlement.features),
          validUntil: entitlement.validUntil,
        }
      : null;
    if (!hasAccountEmbedAccess(identity?.plan, merged, Date.now())) {
      throw new ConvexError("EMBED_ACCESS_REQUIRED");
    }

    if (!args.name.trim()) {
      throw new ConvexError("INVALID_NAME");
    }
    if (!/^wme_[a-f0-9]{5}$/.test(args.keyPrefix)) {
      throw new ConvexError("INVALID_PREFIX");
    }
    if (!/^[a-f0-9]{64}$/.test(args.keyHash)) {
      throw new ConvexError("INVALID_HASH");
    }
    const allowedOrigins = normalizeAllowedOrigins(args.allowedOrigins);

    const active = await ctx.db
      .query("embedKeys")
      .withIndex("by_userId_revokedAt", (q) =>
        q.eq("userId", userId).eq("revokedAt", undefined),
      )
      .collect();
    if (active.length >= MAX_EMBED_KEYS_PER_USER) {
      throw new ConvexError("KEY_LIMIT_REACHED");
    }

View on GitHub (pinned to 7d06c8633d)