tinyhumansai/openhuman · warning

MCP official deep-page walk refused: page={target_page} > MA

Error message

MCP official deep-page walk refused: page={target_page} > MAX_CURSOR_WALK_PAGES={MAX_CURSOR_WALK_PAGES}

What it means

The registry's cursor walk (fetching page after page to reach a deep `target_page` for offset-style pagination emulation) refuses targets beyond `MAX_CURSOR_WALK_PAGES` (50). To show page N of cursor-paginated results, the client must walk cursors 2..N sequentially; beyond 50 that costs 50+ network fetches for one view, so it bails with this guard (a `[mcp-official] walk refused` warn is logged).

Source

Thrown at src/openhuman/mcp/registry/registries/mcp_official.rs:302

/// `None` if the cursor chain ran out before reaching `target_page`.
///
/// Bails after [`MAX_CURSOR_WALK_PAGES`] iterations to keep a single user
/// request from fanning into hundreds of upstream calls.
async fn walk_cursor_for_page(
    config: &Config,
    q: &str,
    limit: u32,
    target_page: u32,
) -> Result<Option<String>> {
    if target_page <= 1 {
        return Ok(None);
    }
    if target_page > MAX_CURSOR_WALK_PAGES {
        tracing::warn!(
            "[mcp-official] walk refused has_query={} target_page={target_page} max={MAX_CURSOR_WALK_PAGES}",
            !q.is_empty()
        );
        anyhow::bail!(
            "MCP official deep-page walk refused: page={target_page} > MAX_CURSOR_WALK_PAGES={MAX_CURSOR_WALK_PAGES}"
        );
    }

    tracing::debug!(
        "[mcp-official] walk start has_query={} q_len={} target_page={target_page} limit={limit}",
        !q.is_empty(),
        q.len()
    );

    let mut cursor: Option<String> = None;
    let mut net_fetches = 0u32;
    let mut cache_fetches = 0u32;
    // We need the cursor that produces `target_page`, which is the cursor
    // returned by the response for `target_page - 1`.
    for page in 1..target_page {
        let cache_key = format!("mcp_official:search:{q}:{page}:{limit}");

View on GitHub (pinned to 7491200858)

Solutions

  1. Cap user-facing page navigation at 50, or narrow the search query so results fit in fewer pages.
  2. Replace jump-to-page with cursor-based next/prev navigation (reuse the returned cursor instead of computing page numbers).
  3. If you truly need the whole catalog, walk cursors forward yourself at a sane rate rather than jumping to a deep page.
  4. Persist the current cursor, not the page number, so restoring state never requests a deep page.

Example fix

// before
let page = user_requested_page; // can be 500
let page = page.clamp(1, 50); // runtime still refuses 500

// after — clamp at the UI boundary AND drive by cursor
const MAX_PAGES = 50;
const page = Math.min(Math.max(1, user_requested_page), MAX_PAGES);
// or: navigate with nextCursor returned by the previous response
Defensive patterns

Strategy: validation

Validate before calling

const MAX_CURSOR_WALK_PAGES: u32 = 50;
let target = requested_page.min(MAX_CURSOR_WALK_PAGES).max(1);
// refuse earlier with a clear message instead of letting the walk bail deep in the client

Prevention

When it happens

Trigger: A user or API consumer requesting `page > 50` of official-registry search results; UIs that compute page numbers from a total-count/limit estimate and land beyond the cap; saved deep-link pagination state.

Common situations: Long result sets with a jump-to-page control; UIs exposing numeric page input without a bound; scripts paginating the whole catalog by incrementing page numbers.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/0286137736b86f45. Report an issue: GitHub.