marmelab/react-admin · critical

Invalid resource key

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.

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.

Appendix: 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)