Skip to content

fix(core): send NextToken when Hub.list_models pages through hub contents - #6263

Open
evakravi wants to merge 1 commit into
aws:masterfrom
evakravi:fix/jumpstart-hub-list-pagination-v3
Open

fix(core): send NextToken when Hub.list_models pages through hub contents#6263
evakravi wants to merge 1 commit into
aws:masterfrom
evakravi:fix/jumpstart-hub-list-pagination-v3

Conversation

@evakravi

Copy link
Copy Markdown
Member

Problem

Hub.list_models() in sagemaker-core never returns on a hub with more than one page of contents. Hub._list_and_paginate_models reads NextToken from each list_hub_contents response but never sends it, so every call requests the first page and the loop never ends. On SageMakerPublicHub (743 models, 8 pages of 100) the call ran for 5 minutes without a result. The loop is the same in sagemaker-core 2.21.0 and in every v2 release with the hub module.

Solution

Pass the token as next_token on each call after the first. Session.list_hub_contents maps it to NextToken. #6262 is the same fix for v2.

Tests

A new unit test, sagemaker-core/tests/unit/jumpstart/hub/test_hub.py, serves each page once and raises KeyError on a second request for the same page. The old loop fails on its second call. On origin/master it fails with KeyError: ('ModelReference', None). On this branch it passes: 1 passed.

cd sagemaker-core && PYTHONPATH=src python -m pytest tests/unit/jumpstart/hub/test_hub.py

Live check with this branch against SageMakerPublicHub in us-west-2: Hub.list_models() returned in 2.6 s after 9 ListHubContents calls (1 ModelReference, 8 Model, 7 with NextToken). It returned 743 unique Model rows, each with OriginalCreationTime.

black==26.3.1 and flake8==7.1.2 pass on the new test. hub.py has 14 pre-existing pydocstyle findings and one pre-existing black diff on origin/master. This change adds none.

Merge Checklist

  • I read the contribution guide.
  • The change is backward compatible.
  • The commits use the repository commit-message format.
  • The change creates no S3 or STS client.
  • The change has regression coverage and adds no dependency.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

…ents

`Hub._list_and_paginate_models` reads `NextToken` from each `list_hub_contents` response but never sends it, so every call requests the first page. On a hub with more than one page the loop never ends. `Hub.list_models()` on `SageMakerPublicHub` (743 models) ran for 5 minutes without a result.

Pass the token as `next_token` on each call after the first. `Session.list_hub_contents` maps it to `NextToken`.

---
X-AI-Prompt: Why does Hub.list_models never return on SageMakerPublicHub, and how do we fix the pagination?
X-AI-Tool: claude-code
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

Claude finished @evakravi's task in 53s —— View job


PR Review: fix(core): send NextToken when Hub.list_models pages through hub contents

  • Read the PR diff (hub.py +2, test_hub.py +59)
  • Verified the fix against Session.list_hub_contents mapping
  • Reviewed correctness, tests, and edge cases

Verdict: LGTM ✅

The diagnosis and fix are correct. _list_and_paginate_models read NextToken from each response into next_token but never fed it back into the request, so every iteration re-fetched page 1 and while first_iteration or next_token never terminated. The two added lines fix exactly that.

I confirmed the token plumbing lines up end-to-end:

  • hub.py:129-130 sets kwargs["next_token"] = next_token before subsequent calls.
  • session_helper.py:2052-2054 reads kwargs.get("next_token") and maps it to the NextToken request field, then calls sagemaker_client.list_hub_contents.

So next_token (SDK) → NextToken (API) is the correct key, and the guard if next_token: correctly skips it on the first page and on empty/falsy tokens.

Things I checked that are fine

  • No cross-call kwargs leakage. list_models invokes _list_and_paginate_models twice (once for ModelReference, once for Model), each with a freshly built dict unpacked via **{...}. Each call gets its own kwargs dict, so mutating kwargs["next_token"] in the reference pass can't leak into the model pass. The residual next_token left in kwargs after the loop is harmless since the loop has already exited.
  • Termination. On the final page the response has no NextToken, so next_token becomes None and the loop exits — no off-by-one or extra empty request.
  • Test quality. The test is well-constructed: pages.pop(...) guarantees each page is served exactly once, so the pre-fix infinite loop fails fast with KeyError instead of hanging, and assert pages == {} confirms every page (including the next_token-bearing ones) was actually requested. It exercises both content types and multi-page (page-2page-3) traversal.

Minor / optional (non-blocking)

  • The test covers the paging mechanics well. If you wanted one more guard, an explicit assertion that the first request for each content type is sent without next_token (the ("...", None) keys already imply it, but only implicitly). Not necessary to merge.
  • hub/ has no __init__.py, but the sibling test_interfaces.py/test_parsers.py live there too, so discovery is consistent with the existing layout — no action needed.

No correctness, security, or backward-compatibility concerns. The change is additive, matches the v2 companion fix (#6262), and has appropriate regression coverage.
fix/jumpstart-hub-list-pagination-v3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants