anthropics / anthropics/anthropic-sdk-python

beta.skills.list hard-capped at 100 items with broken pagination (has_more always false, no cursor)

オープン
#1,391 コメント 1 件 リアクション 2 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
3.9k
フォーク
853
平均マージ
1日 18時間
マージ済み PR(30日)
11

説明

# Bug: `beta.skills.list` is hard-capped at 100 items with broken pagination

## Summary
`client.beta.skills.list(...)` returns at most 100 skills regardless of the `limit` value, and the response always sets `has_more: false` with no usable cursor (`next_page` / `after_id`). On accounts with >100 skills, everything past the first page is unreachable. This causes silent data loss in any workflow that enumerates skills, and produces false "duplicate display_title" 400 errors at create time when the conflict happens to live in the invisible tail.

## Environment
- SDK: `anthropic-python` (latest as of 2026-04-14)
- API: `beta.skills.list`
- Account under test: >100 custom skills

## Repro

```python
import asyncio
from anthropic import AsyncAnthropic

async def main():
c = AsyncAnthropic()
for limit in (10, 50, 100, 500, 1000):
items = []
page = await c.beta.skills.list(limit=limit)
async for item in page:
items.append(item)
print(f"limit={limit}: got {len(items)} items")

asyncio.run(main())
```

**Actual output** (with an account known to hold ~120 skills):

```
limit=10: 100 items
limit=50: 100 items
limit=100: 100 items
limit=500: 100 items
limit=1000: 100 items
```

The SDK's auto-paginator terminates after the first page because the response payload has `has_more: false` and no `next_page` / `after_id`, so it cannot request more. The `limit` parameter appears to be clamped server-side to 100 regardless of requested value.

## Response shape (abbreviated)
```json
{
"data": [ /* exactly 100 items */ ],
"has_more": false,
"first_id": "skill_...",
"last_id": "skill_..."
}
```
Passing `after_id=` does not work to manually paginate either — the SDK exposes no such parameter, and direct HTTP calls we tried return the same first-100 window.

## Impact on real code
We provision ~120 skills per environment via a script that calls `skills.list()` to reconcile against desired state. When the account exceeds 100 skills:

1. The pre-fetch misses some existing skills.
2. `skills.create(display_title=X)` returns a 400 "duplicate display_title" for skill X, even though X wasn't in the `list()` output.
3. We can't look up X's skill_id to `delete` it, because we can't see it.

Our workaround (https://github.com/A79-ai/agentapp/pull/7846) catches the 400, deletes one of the *visible* scoped skills to shrink the total below 100, re-lists to reveal the conflict, deletes it, then retries the create. It works but is fragile and wastes a delete each cycle.

## Ask
1. Fix the `has_more` / cursor contract so `skills.list` actually paginates. Any of:
- Return `has_more: true` + a working `next_page` or `after_id` cursor when the page is full.
- Accept `after_id=` as a request param and honor it.
2. Or, if pagination is intentionally deferred, raise the 100-item hard cap substantially (e.g. 1000) and document the limit.

## Related
- Filed separately: orphan-file GC is blocked by `skills.versions.retrieve` not exposing backing `file_id`s (issue #1390).

Both issues compound: we can't enumerate all skills, and even for the ones we can see we can't tell which files back them.

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。