Skip to content

Feishu drive info falsely reports File not found for files past page 1 (list pagination already fixed in #101572) #93928

Description

@yetval

Summary

In a Feishu drive folder with more files than one API page, feishu_drive action: info returns a false File not found: <token> for any file that lives beyond the first page, and action: list cannot retrieve any page after the first. Both surfaces ignore the has_more / next_page_token cursor the Lark API returns, so files past page 1 are silently inaccessible.

Environment

  • Commit: cc451f9 (origin/main at time of filing)
  • Surface: Feishu plugin feishu_drive tool, list and info actions (extensions/feishu/src/drive.ts, extensions/feishu/src/drive-schema.ts)

Steps to reproduce

  1. Have a Feishu drive folder with more files than fit in one drive.file.list page (the API signals this with has_more: true and a next_page_token).
  2. Call feishu_drive action: info with the file_token of a file that sorts onto page 2 or later.
  3. Or call feishu_drive action: list on that folder and try to fetch the next page.

Expected

  • info follows has_more / next_page_token until it finds the file, then returns its metadata.
  • list accepts a page cursor so the caller can page through the whole folder, and reports whether more pages remain.

Actual

  • info searches only the first page and throws File not found: <token> for any file past page 1, even though the file exists and the API advertised more pages.
  • list returns only the first page. The action schema exposes no page_token, so the returned next_page_token can never be fed back, and pages 2+ are unreachable.

Root cause

getFileInfo (extensions/feishu/src/drive.ts:356) does a single first-page .find() and throws when absent, never following has_more:

async function getFileInfo(client: Lark.Client, fileToken: string, folderToken?: string) {
  // Use list with folder_token to find file info
  const res = await client.drive.file.list({
    params: folderToken ? { folder_token: folderToken } : {},
  });
  if (res.code !== 0) {
    throw new Error(res.msg);
  }

  const file = res.data?.files?.find((f) => f.token === fileToken);
  if (!file) {
    throw new Error(`File not found: ${fileToken}`);
  }
  // ...
}

listFolder (extensions/feishu/src/drive.ts:331) passes no page_token and the list action schema (extensions/feishu/src/drive-schema.ts:25) exposes none, so the next_page_token it does return at drive.ts:352 can never be supplied on a follow-up call:

async function listFolder(client: Lark.Client, folderToken?: string) {
  const validFolderToken = folderToken && folderToken !== "0" ? folderToken : undefined;
  const res = await client.drive.file.list({
    params: validFolderToken ? { folder_token: validFolderToken } : {},
  });
  // ... returns next_page_token but the dispatch (drive.ts:770-773) accepts no cursor to send back
}

The Lark SDK @larksuiteoapi/node-sdk drive.file.list accepts page_size / page_token and returns next_page_token / has_more (and offers a listWithIterator). The plugin reads neither cursor field on this path.

Sibling surfaces

This is the direct sibling-class of the just-merged wiki pagination fix #37626 (commit 5f90f08), which paginated wiki.spaceNode.list / wiki.space.list in wiki.ts + wiki-schema.ts but did not touch drive.ts. The same plugin's comment paths already paginate correctly: list_comments and list_comment_replies expose page_size + page_token in the schema (drive-schema.ts:54-66) and read has_more / page_token from the response (drive.ts:486-487, 519-520). So the drive list / info omission is the lone un-paginated lister in the plugin. Checked the open Feishu PRs (tool-family gates, card/proactive send, topic queue, audit redaction, URL challenge, streaming card coalescing); none touch drive list / info.

Real behavior proof

Behavior addressed: feishu_drive info must find a file that lives past the first drive.file.list page instead of falsely reporting File not found.
Real environment tested: real registerFeishuDriveTools runtime driven end to end through the tool's execute dispatch and getFileInfo, with client.drive.file.list mocked to return the target on page 2 with has_more: true + next_page_token, at commit cc451f9.
Exact steps or command run after this patch: node scripts/run-vitest.mjs extensions/feishu/src/drive.pagination.repro.test.ts (the repro registers the real drive tool, calls action: info for a token that sorts to page 2, and asserts the lister is drained to 2 pages and the file is returned).
Evidence after fix:

# OBSERVED (buggy, origin/main cc451f98cbae)
 FAIL  extensions/feishu/src/drive.pagination.repro.test.ts > info finds a file that lives past the first page
 AssertionError: expected "vi.fn()" to be called 2 times, but got 1 times
   115|     expect(listMock).toHaveBeenCalledTimes(2);
       |                      ^
 -> getFileInfo queried only page 1 (one list call) and threw "File not found: target";
    list was never asked for page 2 despite has_more: true.

# EXPECTED (correct behavior on identical inputs, with the local fix applied)
 Test Files  2 passed (2)
      Tests  16 passed (16)
 -> getFileInfo drains has_more/next_page_token to page 2 (two list calls), finds and
    returns the file. Existing drive.test.ts stays green (15 tests), 16 total.

Observed result after fix: on unmodified main, info silently fails for files past page 1 (list called once, throws File not found); draining pages following has_more/next_page_token fixes it without regressing existing drive behavior.
What was not tested: not run against a live Feishu tenant; the proof drives the real plugin code path with the Lark drive.file.list response shape mocked rather than a live multi-page folder. The list action was not exercised in the automated repro (its defect is structural: the schema exposes no page_token cursor, shown above), only info was driven end to end.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.no-staleExclude from stale automation

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions