marmelab/react-admin · error

Invalid resource key: ${resource}

Error message

Invalid resource key: ${resource}

What it means

The ra-data-local-storage provider throws this error to protect against prototype pollution. Resource keys in JavaScript objects are stored as object properties, so keys like '__proto__', 'constructor', or 'prototype' could overwrite Object.prototype and corrupt every object in the app. checkResource is called by the provider's data methods before any storage access to reject such keys.

Source

Thrown at packages/ra-data-local-storage/src/index.ts:172

            return baseDataProvider.delete<RecordType>(resource, params);
        },
        deleteMany: (resource, params) => {
            checkResource(resource);
            updateLocalStorage(() => {
                const indexes = params.ids.map(id =>
                    data[resource]?.findIndex(record => record.id == id)
                );
                pullAt(data[resource], indexes);
            });
            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 LocalStorageDataProviderParams {
    defaultData?: any;
    localStorageKey?: string;
    loggingEnabled?: boolean;
    localStorageUpdateDelay?: number;
}

View on GitHub (pinned to 051f511bb0)

Solutions

  1. Audit where the resource argument comes from and stop passing '__proto__', 'constructor', or 'prototype' as resource names
  2. Validate resource names against your app's known resource list before calling the provider
  3. Sanitize dynamic resource identifiers extracted from URLs or user input with an allowlist

Example fix

// before
const resource = window.location.hash.slice(1);
dataProvider.getList(resource, params);
// after
const resource = window.location.hash.slice(1);
const allowed = ['posts', 'comments', 'users'];
if (!allowed.includes(resource)) throw new Error(`Unknown resource: ${resource}`);
dataProvider.getList(resource, params);
Defensive patterns

Strategy: validation

Validate before calling

const FORBIDDEN = ['__proto__', 'constructor', 'prototype'];
const assertSafeResource = resource => {
    if (typeof resource !== 'string' || FORBIDDEN.includes(resource)) {
        throw new Error(`Invalid resource key: ${resource}`);
    }
};
assertSafeResource(resource);
dataProvider.getList(resource, params);

Type guard

const isSafeResource = (r: unknown): r is string =>
    typeof r === 'string' && !['__proto__', 'constructor', 'prototype'].includes(r);

Try / catch

try {
    await dataProvider.getList(resource, params);
} catch (e) {
    if (e instanceof Error && e.message.startsWith('Invalid resource key')) {
        console.error('Blocked unsafe resource key:', resource);
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling any provider method (getList, getOne, create, update, delete, etc.) with resource set to '__proto__', 'constructor', or 'prototype', e.g. dataProvider.getList('__proto__', ...) or a List resource name="__proto__" propagated into provider calls.

Common situations: Dynamic resource names derived from URL path segments or user input that reach the data provider unvalidated; accidentally passing a variable that is undefined-adjacent or a literal meta-property; security tests probing for prototype pollution in a localStorage-backed admin.

Related errors


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