strapi/strapi · error · ValidationError

Invalid key ${key}

Error message

Invalid key ${key}

What it means

Thrown by the same throwInvalidKey helper as error 80, but without a nested path suffix. The plain 'Invalid key ${key}' message is emitted when the violating key is at the top level of the query (path equals key or is absent), so no location qualifier is appended. It represents the same RBAC/schema validation failure class: a visitor detected a disallowed field during filters/sort/populate/fields traversal.

Source

Thrown at packages/core/admin/server/src/services/permission/permissions-manager/validate.ts:39

  contentTypes;
const {
  ID_ATTRIBUTE,
  DOC_ID_ATTRIBUTE,
  CREATED_AT_ATTRIBUTE,
  UPDATED_AT_ATTRIBUTE,
  PUBLISHED_AT_ATTRIBUTE,
  CREATED_BY_ATTRIBUTE,
  UPDATED_BY_ATTRIBUTE,
} = constants;

const COMPONENT_FIELDS = ['__component'];

const STATIC_FIELDS = [ID_ATTRIBUTE, DOC_ID_ATTRIBUTE];

const throwInvalidKey = ({ key, path }: { key: string; path?: string | null }) => {
  const msg = path && path !== key ? `Invalid key ${key} at ${path}` : `Invalid key ${key}`;

  throw new ValidationError(msg);
};

export default ({ action, ability, model }: any) => {
  const schema = strapi.getModel(model);

  // Create request-scoped model cache to avoid redundant getModel() calls
  const modelCache = createModelCache(strapi.getModel.bind(strapi));

  const ctx = {
    schema,
    getModel: modelCache.getModel,
  };

  const createValidateQuery = (options = {} as any) => {
    const { fields } = options;

    // TODO: validate relations to admin users in all validators
    const permittedFields = fields.shouldIncludeAll ? null : getQueryFields(fields.permitted);

View on GitHub (pinned to 4a4101264d)

Solutions

  1. Remove the offending key named in the message from the query.
  2. Grant the user's role read access to that field via the admin RBAC settings.
  3. If the field is hidden or private, switch to an allowed field or unhide it in the content-type schema.
  4. Sanitize client-supplied query parameters against the content-type's writable/visible attribute list before forwarding.

Example fix

// before
strapi.documents('api::article.article').findMany({
  fields: ['title', 'internalNotes'], // internalNotes not permitted
});
// after
strapi.documents('api::article.article').findMany({
  fields: ['title'],
});
Defensive patterns

Strategy: validation

Validate before calling

const visible = strapi.contentTypes[uid] ? Object.entries(strapi.contentTypes[uid].attributes).filter(([,a])=>!a.hidden).map(([k])=>k) : [];
const safeFields = (query.fields || []).filter((f) => visible.includes(f));

Prevention

When it happens

Trigger: A top-level query key violates the permission visitors: a disallowed field name in fields:[...], a top-level filter on a non-permitted attribute, a hidden attribute, a password field, or a disallowed admin::user field. Distinguished from error 80 only by the absence of a distinct nested path.

Common situations: Calling content-manager endpoints with a fields array or populate object that references a field the current role cannot read. A plugin auto-injecting query params that breach RBAC. Migrating from CE to EE where field-level permissions tighten enforcement.

Related errors


AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12). Data as JSON: /api/errors/41b9d616db20cf2f. Report an issue: GitHub.