marmelab/react-admin · critical

Invalid resource key: ${resource}

Error message

Invalid resource key: ${resource}

What it means

checkResource() blocks resource names that could enable prototype pollution: `__proto__`, `constructor`, and `prototype`. Because the provider writes to `data[resource]` and persists to localForage keys built from the resource name, a malicious or buggy resource string with these values could corrupt Object prototypes. Any write method (update, updateMany, create, delete, deleteMany) throws this before doing anything else.

Source

Thrown at packages/ra-data-local-forage/src/index.ts:280

            const indexes = params.ids.map((id: any) => {
                if (!data) {
                    throw new Error('The dataProvider is not initialized.');
                }
                return data[resource].findIndex(
                    (record: any) => record.id === id
                );
            });
            pullAt(data[resource], indexes);
            updateLocalForage(resource);
            return baseDataProvider.deleteMany(resource, params);
        },
    };
};

const checkResource = resource => {
    if (['__proto__', 'constructor', 'prototype'].includes(resource)) {
        // protection against prototype pollution
        throw new Error(`Invalid resource key: ${resource}`);
    }
};

export interface LocalForageDataProviderParams {
    defaultData?: any;
    prefixLocalForageKey?: string;
    loggingEnabled?: boolean;
}

View on GitHub (pinned to 051f511bb0)

Solutions

  1. Audit where resource strings originate; never pass raw URL/URLSearchParams values as the resource.
  2. Validate resources against an allowlist of known resource names before calling dataProvider methods.
  3. Sanitize dynamic routes so `__proto__`/`constructor`/`prototype` segments never reach the dataProvider.
  4. Keep ra-data-local-forage updated so the pollution guard stays in place.

Example fix

// before: resource straight from URL
const { resource } = useParams();
await dataProvider.update(resource as string, params);

// after: allowlist check
const allowed = ['posts', 'comments'];
if (!allowed.includes(resource)) throw new Error('Unknown resource');
await dataProvider.update(resource as string, params);
Defensive patterns

Strategy: validation

Validate before calling

const RESOURCE_ALLOWLIST = new Set(['posts', 'comments', 'tags']);
export function assertSafeResource(resource: string): void {
  if (!RESOURCE_ALLOWLIST.has(resource)) {
    throw new Error(`Unknown resource: ${resource}`);
  }
  if (['__proto__', 'constructor', 'prototype'].includes(resource)) {
    throw new Error(`Invalid resource key: ${resource}`);
  }
}
// call before every dataProvider invocation
assertSafeResource(resource);

Type guard

function isSafeResource(r: string): boolean {
  return !['__proto__', 'constructor', 'prototype'].includes(r);
}

Try / catch

try {
  await dataProvider.update(resource, params);
} catch (e) {
  if (e.message.startsWith('Invalid resource key')) {
    notify('Invalid resource — refusing to persist data');
    return; // never retry; this indicates hostile/malformed input
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any mutating dataProvider method with resource set to `__proto__`, `constructor`, or `prototype` — typically from dynamic route parameters (e.g. /admin/__proto__/edit), user-controlled refs, or misconfigured resources derived from API input.

Common situations: Apps that build resource names from URL segments or remote config without validation; security scanners probing for prototype-pollution sinks; typos in resource definitions are NOT caught here (only the three dangerous keys).

Related errors


AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30). Data as JSON: /api/errors/c7220638e5765e6a. Report an issue: GitHub.