Skip to content

fix: derive provider_tokens_set from local git provider cache#123

Merged
hieptl merged 1 commit into
mainfrom
hieptl/fix-git-integrations
May 6, 2026
Merged

fix: derive provider_tokens_set from local git provider cache#123
hieptl merged 1 commit into
mainfrom
hieptl/fix-git-integrations

Conversation

@hieptl
Copy link
Copy Markdown
Contributor

@hieptl hieptl commented May 6, 2026

Description

After saving a GitHub Personal Access Token on /settings/integrations, the UI never enters the "GitHub configured" state and direct GitHub API calls (e.g. getUser, suggested tasks, repo/branch pickers) never fire — even though the token is persisted on both the agent server and in localStorage.

Steps to reproduce

  1. Run the agent server and start the GUI on http://localhost:3001.
  2. Navigate to http://localhost:3001/settings/integrations.
  3. Paste a GitHub PAT into the GitHub token field.
  4. Click Save Changes. Toast shows "Settings saved."
  5. Refresh the page or open the chat repo picker.

Expected behavior

  • The GitHub row on /settings/integrations shows the configured-token UI (host displayed, token masked, Disconnect Tokens enabled).
  • Repo / branch pickers and suggested-tasks calls authenticate against api.github.com using the saved token.

Actual behavior

  • The save round-trip succeeds (PUT /api/settings/secrets returns 200).
  • The token is persisted server-side (workspace/.openhands/secrets.json) and locally (localStorage["openhands-agent-server-git-provider-tokens"]).
  • However, the GUI continues to render the "not configured" state and emits zero requests to GitHub.

Root cause

PR #98 (commit 7fe650d) migrated git provider tokens off the legacy PATCH /api/settings { provider_tokens_set } flow and onto the agent server's PUT /api/settings/secrets endpoint. Two halves of that migration were left undone:

  1. The agent server's SettingsResponse model never returns a provider_tokens_set field — by design (see comment in openhands/agent_server/persistence/models.py:262-265: the agent server tracks only custom secrets and does not own provider tokens).
  2. The GUI still reads settings.provider_tokens_set.<provider> everywhere — useUserProviders (src/hooks/use-user-providers.ts:8-11), routes/git-settings.tsx:62-75, git-control-bar-repo-button.tsx:30, git-control-bar-branch-button.tsx:24. With the field permanently absent from the response, useUserProviders().providers always returns [], gating off every downstream GitHub interaction.

Out of scope (separate follow-up)

The agent runtime cloning code (software-agent-sdk/openhands-workspace/openhands/workspace/cloud/repo.py:40-45) looks for canonical secret names (github_token, gitlab_token, …) while the GUI saves GIT_PROVIDER_GITHUB_TOKEN. Server-side git clone will still fail to authenticate even after this fix lands. Track separately

Summary

  • Restores the post-save "GitHub configured" UI state on /settings/integrations and unblocks every downstream GitHub API call in the GUI (repo picker, branch picker, suggested tasks).
  • Derives settings.provider_tokens_set from the existing localStorage cache that SecretsService.addGitProvider already populates after each successful server save — no new HTTP calls, no backend changes.

Why

PR #98 moved git provider tokens onto the agent-server PUT /api/settings/secrets endpoint, but the agent-server's SettingsResponse does not return a provider_tokens_set field (intentional — see openhands/agent_server/persistence/models.py:262-265). The GUI still reads settings.provider_tokens_set.<provider> from useUserProviders, routes/git-settings.tsx, and the chat git-control-bar buttons. With the field permanently absent, useUserProviders().providers === [] and every GitHub-gated code path remained inert even though the token had been saved on both the server and in localStorage.

What changed

src/api/settings-service/settings-service.api.tssyncDerivedSettings now folds getStoredGitProviders() into provider_tokens_set. The map is re-derived on every call (including in-memory cache hits), so the existing React-Query invalidation in useAddGitProviders.onSuccess immediately surfaces the configured state to all consumers.

Why localStorage and not GET /api/settings/secrets

  • SecretsService.addGitProvider writes server-side first; localStorage is only updated on success (src/api/secrets-service.ts:303-315). So localStorage presence is already a reliable signal that the secret is on the server.
  • localStorage carries host alongside token; the secrets list endpoint returns only name/description, which would force fragile description parsing.
  • The frontend's GitHub API client already reads localStorage via getStoredGitProviderToken (src/api/git-providers/provider-handler.ts:64-68), keeping both consumers aligned on a single source of truth.

@hieptl hieptl self-assigned this May 6, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-server-gui Ready Ready Preview, Comment May 6, 2026 5:50pm

Request Review

@hieptl hieptl changed the title feat: derive provider_tokens_set from local git provider cache fix: derive provider_tokens_set from local git provider cache May 6, 2026
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste - Elegant, pragmatic solution

This PR fixes a real production bug where GitHub integrations stopped working after PR #98. The solution correctly derives provider_tokens_set from localStorage, which is already the source of truth for git provider tokens. The implementation is simple, well-tested, and doesn't introduce any risks.

[RISK ASSESSMENT]
⚠️ Risk Assessment: 🟢 LOW

This is a straightforward bug fix that:

  • Restores broken functionality without changing APIs
  • Reuses existing localStorage data (already populated by SecretsService)
  • Includes proper test coverage
  • Has no agent behavior or eval/benchmark impact

VERDICT:
Worth merging - Clean fix with no issues

KEY INSIGHT:
Deriving provider_tokens_set from localStorage is the right call - it's already the authoritative source for git provider tokens after successful server saves, and this approach avoids adding unnecessary backend API calls.

@hieptl hieptl merged commit d3f5a94 into main May 6, 2026
5 checks passed
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.

2 participants