anthropics / anthropics/anthropic-sdk-python

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

Aperta
#1,391 1 commento 2 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Python
Stelle
3.9k
Fork
853
Merge medio
1g 18h
PR unite (30g)
11

Descrizione

# 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.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.