github/copilot-sdk · error · InvalidOperationException

GitHub token provider returned neither a token nor…

Error message

GitHub token provider returned neither a token nor cancellation.

What it means

CopilotClient calls a user-supplied GitHub token provider (the `tokenProvider` callback) to acquire an access token. The provider must return either a token or a cancelled result. When it returns a result object that is neither cancelled nor carries a token, the client cannot proceed and throws this InvalidOperationException.

Solutions

  1. Fix the token provider so it returns a populated result with a non-null Token on success.
  2. When authentication cannot proceed, return a cancelled result (GitHubTokenAcquireResultCancelled) or null instead of an empty result object.
  3. Add logging inside the provider to verify why Token was null (expired credential, missing scope, network failure).
  4. Wrap the provider's token source (e.g. `gh auth token`, device flow) in validation before returning.

Example fix

// before
return new GitHubTokenAcquireResult(); // empty, no token, not cancelled

// after
var token = await AcquireTokenAsync();
return token is null
    ? new GitHubTokenAcquireResultCancelled()
    : new GitHubTokenAcquireResultToken { AccessToken = token, TokenType = "bearer" };
Defensive patterns

Strategy: validation

Validate before calling

var result = provider.Acquire();
if (result is not { Cancelled: true } && result?.Token is null)
    throw new InvalidOperationException("Token provider must return a token or cancel");

Type guard

static bool HasUsableToken(GitHubTokenAcquireResult? r) => r is { Cancelled: false, Token.AccessToken: { Length: > 0 } };

Try / catch

try
{
    await client.StartAsync();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("neither a token nor cancellation"))
{
    logger.LogError(ex, "GitHub token provider misconfigured");
}

Prevention

When it happens

Trigger: The registered GitHub token provider callback returns a result object (e.g. a GitHubTokenAcquireResult with a null/missing Token and Cancelled=false), so `result?.Token is not { } token` matches and the throw at Client.cs:2119 fires.

Common situations: A custom token provider that returns a default-constructed/empty result after a failed internal lookup; a provider that logs an error but still returns a non-null, non-cancelled result instead of returning null or cancelling; providers built from partially parsed responses where AccessToken was never populated.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/a508b39d715217b0. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/Client.cs:2119

            var reason = request.Reason == GitHubTokenAcquireReason.Initial
                ? GitHubTokenRequestReason.Initial
                : request.Reason == GitHubTokenAcquireReason.Refresh
                    ? GitHubTokenRequestReason.Refresh
                    : throw new InvalidOperationException($"Unknown GitHub token request reason '{request.Reason}'.");
            var result = await provider(new GitHubTokenProviderArgs
            {
                Host = request.Host,
                SessionId = request.SessionId,
                Reason = reason,
            }).ConfigureAwait(false);

            if (result is { Cancelled: true })
            {
                return new GitHubTokenAcquireResultCancelled();
            }
            if (result?.Token is not { } token)
            {
                throw new InvalidOperationException(
                    "GitHub token provider returned neither a token nor cancellation.");
            }
            return new GitHubTokenAcquireResultToken
            {
                AccessToken = token.AccessToken,
                TokenType = token.TokenType,
                ExpiresIn = token.ExpiresIn,
            };
        }
    }

    /// <summary>
    /// Tells the runtime to route its outbound model-layer requests through this
    /// client's LLM inference provider. No-op when interception is not configured.
    /// </summary>
    private async Task ConfigureLlmInferenceAsync(CancellationToken cancellationToken)
    {
        if (_clientGlobalApis?.LlmInference is null)

View on GitHub (pinned to cd8cf15dc3)