rohitg00/agentmemory · error · Error

mem::search: limit must be a positive integer

Error message

mem::search: limit must be a positive integer

What it means

mem::search accepts an optional limit that must be a positive integer (default 20, capped at MAX_LIMIT 100). Non-integers, zero, negative numbers, and non-numeric values are rejected with this error before any search runs.

Source

Thrown at src/functions/search.ts:391

      limit?: number
      project?: string
      cwd?: string
      format?: string
      token_budget?: number
      agentId?: string
    }) => {
      const idx = getSearchIndex()

      // Input validation / normalization.
      if (typeof data?.query !== 'string' || !data.query.trim()) {
        throw new Error('mem::search: query must be a non-empty string')
      }
      const query = data.query.trim()
      const MAX_LIMIT = 100
      let effectiveLimit = 20
      if (data.limit !== undefined) {
        if (!Number.isInteger(data.limit) || data.limit < 1) {
          throw new Error('mem::search: limit must be a positive integer')
        }
        effectiveLimit = Math.min(data.limit, MAX_LIMIT)
      }
      const projectFilter = typeof data.project === 'string' && data.project.trim().length > 0 ? data.project.trim() : undefined
      const cwdFilter = typeof data.cwd === 'string' && data.cwd.trim().length > 0 ? data.cwd.trim() : undefined
      // #817: agent-scope isolation. mem::search backs REST /search,
      // memory_recall and recall_context. Without filtering here a
      // worker booted with AGENT_ID=B + AGENTMEMORY_AGENT_SCOPE=isolated
      // could read A's memories — the cross-agent leak the issue
      // documented. Mirrors the smart-search pattern: wildcard "*"
      // bypasses, explicit agentId pins, isolated mode falls back to
      // the worker's own AGENT_ID.
      //
      // Fail-closed: if isolated mode is on AND no explicit agentId
      // is given AND env AGENT_ID is unset, refuse the call rather
      // than silently dropping the filter. Allowing the call through
      // with filterAgentId=undefined is the same leak this fix is
      // supposed to close.

View on GitHub (pinned to e04ba88819)

Solutions

  1. Omit limit entirely to use the default of 20.
  2. Coerce and validate: pass Math.floor(Number(v)) only when it is a positive integer.
  3. Remember values above 100 are clamped (not rejected) — only non-positive-integers throw.
  4. Fix query-string parsing to convert numeric strings to numbers.

Example fix

// before
const limit = Number(searchParams.get('limit')); // NaN or 0 possible
await trigger({ function_id: 'mem::search', payload: { query: q, limit } });
// after
const raw = Number(searchParams.get('limit'));
const limit = Number.isInteger(raw) && raw >= 1 ? raw : undefined;
await trigger({ function_id: 'mem::search', payload: { query: q, limit } });
Defensive patterns

Strategy: validation

Validate before calling

function toLimit(v: unknown): number | undefined {
  if (v === undefined || v === null) return undefined;
  const n = Number(v);
  return Number.isInteger(n) && n >= 1 ? n : undefined;
}

Type guard

function isPositiveInt(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v >= 1;
}

Try / catch

try {
  return await trigger({ function_id: 'mem::search', payload: { query, limit } });
} catch (e) {
  if (String(e.message).includes('limit must be a positive integer')) {
    return await trigger({ function_id: 'mem::search', payload: { query } });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling mem::search with limit: 0, limit: -5, limit: 10.5, limit: '20' (string), or NaN — any value that is defined but fails Number.isInteger(v) && v >= 1.

Common situations: Passing a page size of 0 to mean 'no limit'; forwarding a string from a query-string parameter without Number() coercion; floating-point results from a size calculation.

Related errors


AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30). Data as JSON: /api/errors/24a42665d015dc04. Report an issue: GitHub.