refinedev/refine · error · Error

Max deep reached

Error message

Max deep reached

What it means

Thrown by refine's Appwrite data provider when a CrudFilter logical operator chain (and/or/nested) exceeds the maximum recursion depth of 10 in generateFilter. It prevents infinite or pathological recursion while translating refine filters into Appwrite Query strings.

Source

Thrown at packages/appwrite/src/utils/generateFilter.ts:15

import type { CrudFilter } from "@refinedev/core";
import { Query } from "appwrite";
import { replaceIdWithAppwriteId } from "./replaceIdWithAppwriteId";

/**
 * Generate a filter string for Appwrite from Refine's filter
 * @param filter Refine's filter
 * @param deep Max deep of the filter
 * @returns Appwrite's filter string
 */
export const generateFilter = (filter: CrudFilter, deep = 10): string => {
  const nextDeep = deep - 1;

  if (nextDeep < 0) {
    throw new Error("Max deep reached");
  }

  filter = replaceIdWithAppwriteId(filter);

  switch (filter.operator) {
    // Logical operators
    case "eq":
      return Query.equal(filter.field, filter.value);
    case "ne":
      return Query.notEqual(filter.field, filter.value);
    case "gt":
      return Query.greaterThan(filter.field, filter.value);
    case "gte":
      return Query.greaterThanEqual(filter.field, filter.value);
    case "lt":
      return Query.lessThan(filter.field, filter.value);
    case "lte":
      return Query.lessThanEqual(filter.field, filter.value);

View on GitHub (pinned to 779d52a20e)

Solutions

  1. Flatten the filter tree so logical nesting stays under 10 levels (e.g. merge nested 'or' branches into one level)
  2. If filters are machine-generated, cap or simplify the generated structure before passing it to the data provider
  3. As a last resort, fork/patch generateFilter to raise the deep limit

Example fix

// before
const f = { operator: 'or', value: [{ operator: 'or', value: [{ operator: 'or', value: [/* ...deep... */] }] }] };
dataProvider.getList({ resource: 'posts', filters: [f] });
// after
const f = { operator: 'or', value: [cond1, cond2, cond3] }; // flattened, depth < 10
dataProvider.getList({ resource: 'posts', filters: [f] });
Defensive patterns

Strategy: validation

Validate before calling

const filterDepth = (f) => f.operator === 'and' || f.operator === 'or' ? 1 + Math.max(0, ...f.value.map(filterDepth)) : 0;
if (filterDepth(filter) >= 10) throw new Error('Filter too deep — flatten before sending');
await dataProvider.getList({ resource: 'posts', filters: [filter] });

Type guard

const isLogical = (f) => f.operator === 'and' || f.operator === 'or';

Try / catch

try { await dataProvider.getList({ resource, filters }); } catch (e) { if (e.message === 'Max deep reached') { /* flatten filters and retry */ } else throw e; }

Prevention

When it happens

Trigger: Passing a filter with more than 10 levels of nested logical operators (and/or) to the Appwrite data provider's getList, e.g. filter: { operator: 'or', value: [{ operator: 'or', value: [...10+ levels deep...] }] }.

Common situations: Programmatically generated filters (e.g. from a filter builder UI or a loop) that nest logical groups more than 10 levels; converting deeply nested filters from another data provider to Appwrite.

Related errors


AI-assisted analysis of refinedev/refine@779d52a20e (2026-08-27). Data as JSON: /api/errors/09e7fd189c80a2de. Report an issue: GitHub.