Skip to content

[Bug][github_graphql] GitHub App installation token not refreshed for GraphQL client, panic and pipeline crash on large repos #8788

Closed

Description

@danielemoraschi

Search before asking

  • I had searched in the issues and found no similar issues.

What happened

When collecting data from a large repository using GitHub App authentication, the pipeline crashes after approximately 1 hour with the following panic:

panic: non-200 OK status code: 401 Unauthorized body: "{
  \"message\": \"Bad credentials\",
  \"documentation_url\": \"https://docs.github.com/rest\",
  \"status\": \"401\"
}"
goroutine 11186 [running]:
github.com/apache/incubator-devlake/helpers/pluginhelper/api.(*GraphqlAsyncClient).updateRateRemaining.func1()
	/app/helpers/pluginhelper/api/graphql_async_client.go:129
created by github.com/apache/incubator-devlake/helpers/pluginhelper/api.(*GraphqlAsyncClient).updateRateRemaining
	/app/helpers/pluginhelper/api/graphql_async_client.go:117

There are two independent bugs that combine to cause this:

Bug 1: Process crash on any error in the rate-limit goroutine

graphql_async_client.go:129 calls panic(err) inside the background goroutine that periodically polls the GitHub GraphQL rate limit. Any error, including a transient 401, immediately kills the entire DevLake process.

Bug 2: GraphQL HTTP client holds a frozen, never-refreshed token (root cause)

In backend/plugins/github_graphql/impl/impl.go (lines 183-209), the GraphQL HTTP client is built with oauth2.StaticTokenSource:

tokens := strings.Split(connection.Token, ",")
src := oauth2.StaticTokenSource(
    &oauth2.Token{AccessToken: tokens[0]},
)
httpClient := oauth2.NewClient(oauthContext, src)

This freezes the token to the value at task start time. The client has no connection to the TokenProvider / RefreshRoundTripper machinery that already handles token refresh for the REST client.

By contrast, CreateApiClient (called just above at line 173) correctly wires up NewAppInstallationTokenProvider + RefreshRoundTripper and auto-refreshes on 401. This was added in #8746. The GraphQL client was not updated as part of that PR and still bypasses the refresh infrastructure with its own isolated oauth2 HTTP client.

Since GitHub App installation tokens expire after 1 hour, any pipeline running longer than that will encounter 401s on all GraphQL requests. The rate-limit polling goroutine then hits the 401 and panics, crashing the process.

Note: the documentation page at https://devlake.apache.org/docs/Configuration/GitHub/#github-apps-beta currently states "The GitHub App token expires every hour and does not auto-renew." This was accurate before #8746 but is now stale for the REST path. The GraphQL path is the remaining gap.

What do you expect to happen

  • The GitHub App installation token should be transparently refreshed for GraphQL requests, the same way it already is for REST requests.
  • If token refresh fails or any other error occurs in the background rate-limit goroutine, the error should be logged and retried, not cause a process-wide panic.
  • Pipelines against large repositories should complete successfully without requiring users to fall back to Personal Access Tokens.

How to reproduce

  1. Configure a GitHub connection using GitHub App authentication (not PAT).
  2. Create a blueprint targeting a large repository whose full collection takes more than 1 hour.
  3. Trigger the pipeline.
  4. After ~1 hour, observe the panic in the logs and the pipeline crashing.

Anything else

Affected files:

File Issue
backend/helpers/pluginhelper/api/graphql_async_client.go:129 panic(err) in background goroutine crashes the process on any rate-limit poll error
backend/plugins/github_graphql/impl/impl.go:183-209 oauth2.StaticTokenSource freezes the token at task start; GraphQL client is not wired to TokenProvider / RefreshRoundTripper

Suggested fix:

  1. In graphql_async_client.go, replace panic(err) with graceful error handling (log the error and reschedule the next poll) so a transient 401 does not crash the process.

  2. In github_graphql/impl/impl.go, instead of creating a new oauth2.StaticTokenSource-backed HTTP client, reuse apiClient.GetClient() -- the REST client's underlying *http.Client -- which already carries the RefreshRoundTripper. This is safe: ApiAsyncClient embeds *ApiClient which exposes GetClient(), and proxy is already configured inside NewApiClientFromConnection so the oauthContext proxy block becomes redundant. Token injection is handled by the RefreshRoundTripper, making the oauth2 wrapper unnecessary.

Related: #8746 (added token refresh for REST path)

Version

v1.0.3-beta10

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. added
    component/pluginsThis issue or PR relates to plugins
    priority/highThis issue is very important
    severity/p0This bug blocks key user journey and function
    on Mar 20, 2026
  2. Ke-vin-S commented on Mar 20, 2026

    @Ke-vin-S
    Contributor

    Hi @danielemoraschi, thanks for the clear description. I'd like to work on this issue and can submit a PR soon. Could a maintainer please assign it to me?

  3. added 2 commits that reference this issue on Mar 22, 2026
    f1d24c1
    39d017d
  4. klesh commented on Mar 23, 2026

    @klesh
    Contributor

    @Ke-vin-S Thanks for picking this up.

  5. added 4 commits that reference this issue on Apr 8, 2026
    551e7f7
    dd7955e
    88fc30c
    b588955
  6. github-actions commented on May 23, 2026

    @github-actions
    Contributor

    This issue has been automatically marked as stale because it has been inactive for 60 days. It will be closed in next 7 days if no further activity occurs.

  7. Ke-vin-S commented on May 26, 2026

    @Ke-vin-S
    Contributor

    actively working on this

  8. added a commit that references this issue on Jun 12, 2026
    61d66a1
  9. added a commit that references this issue on Jun 18, 2026
    bb5cdb7
  10. added a commit that references this issue on Jul 30, 2026
    80c283f
  11. added a commit that references this issue on Oct 19, 2026
    d585d89
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

component/pluginsThis issue or PR relates to pluginspriority/highThis issue is very importantseverity/p0This bug blocks key user journey and functiontype/bugThis issue is a bug

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions