marmelab/react-admin · error · Error

useDeleteMany mutation requires a resource

Error message

useDeleteMany mutation requires a resource

What it means

useDeleteMany wraps dataProvider.deleteMany in a react-query mutation. The mutate function accepts the resource as its first argument; if it is null or undefined, react-admin cannot know which resource to delete records from, so it throws immediately instead of calling the data provider. This is a programming-error guard against calling mutate() without a resource.

Source

Thrown at packages/ra-core/src/dataProvider/useDeleteMany.ts:104

>(
    resource?: string,
    params: Partial<DeleteManyParams<RecordType>> = {},
    options: UseDeleteManyOptions<RecordType, MutationError> = {}
): UseDeleteManyResult<RecordType, MutationError> => {
    const dataProvider = useDataProvider();
    const queryClient = useQueryClient();
    const {
        mutationMode = 'pessimistic',
        mutationFn: customMutationFn,
        onSettled,
        ...mutationOptions
    } = options;
    const customMutationFnWithDataProviderResult = async (
        resource: string | undefined,
        params: Omit<UseDeleteManyMutateParams<RecordType>, 'resource'>
    ) => {
        if (resource == null) {
            throw new Error('useDeleteMany mutation requires a resource');
        }
        if (params.ids == null) {
            throw new Error('useDeleteMany mutation requires an array of ids');
        }
        if (customMutationFn == null) {
            return dataProvider.deleteMany<RecordType>(
                resource,
                params as DeleteManyParams<RecordType>
            );
        }

        return {
            data: await customMutationFn({ resource, ...params }),
        };
    };

    const [mutate, mutationResult] = useMutationWithMutationMode<
        MutationError,

View on GitHub (pinned to 051f511bb0)

Solutions

  1. Pass the resource string as the first argument to mutate: mutate('posts', { ids }).
  2. If the resource comes from useResourceContext() or route params, render the component only inside the resource route or provide a default.
  3. Guard with an early check: if (!resource) return; before calling mutate.

Example fix

// before
const [deleteMany] = useDeleteMany();
deleteMany(resource, { ids }); // resource may be undefined
// after
const resource = useResourceContext();
const [deleteMany] = useDeleteMany();
if (resource) deleteMany(resource, { ids });
Defensive patterns

Strategy: validation

Validate before calling

if (typeof resource !== 'string' || resource.length === 0) {
  throw new TypeError('useDeleteMany: resource must be a non-empty string');
}

Type guard

const hasResource = (r: unknown): r is string => typeof r === 'string' && r.length > 0;

Try / catch

try {
  await mutate(resource!, { ids });
} catch (e) {
  if (e instanceof Error && e.message.includes('requires a resource')) {
    notify('Cannot delete: no resource selected', { type: 'warning' });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling mutate(undefined, { ids: [...] }) or mutate(null, {...}); passing a resource variable that is undefined because it came from an unmatched route param or an uninitialized prop; destructuring useDeleteMany and invoking mutate before the resource prop is set.

Common situations: Dynamic resource names read from useParams() that are undefined outside the resource route; a custom list toolbar component reused outside a Resource context; refactoring that moved the mutate call to a place where the resource string is no longer in scope.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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