Adding repo to team fails with "Unauthorized. Please check your token and try again" even when the token has repo permissions
- Dominant language
- C#
- Stars
- 478
- Forks
- 146
- Avg merge
- 7d 21h
- Merged PRs (30d)
- 2
Description
## Description
I am using gh ado2gh to test migration of our repos from DevOps to GHE. But the migration script fails with error 401 on step, where the migrated repository is added to created team. I have used generate-script with --sequential to make the migration sequential for purpose of the test.
## Reproduction Steps
Used script:
```powershell
#!/usr/bin/env pwsh
# =========== Created with CLI version 1.17.0 ===========
function Exec {
param (
[scriptblock]$ScriptBlock
)
& @ScriptBlock
if ($lastexitcode -ne 0) {
exit $lastexitcode
}
}
if (-not $env:ADO_PAT) {
Write-Error "ADO_PAT environment variable must be set to a valid Azure DevOps Personal Access Token with the appropriate scopes. For more information see https://docs.github.com/en/migrations/using-github-enterprise-importer/preparing-to-migrate-with-github-enterprise-importer/managing-access-for-github-enterprise-importer#personal-access-tokens-for-azure-devops"
exit 1
}
else {
Write-Host "ADO_PAT environment variable is set and will be used to authenticate to Azure DevOps."
}
if (-not $env:GH_PAT) {
Write-Error "GH_PAT environment variable must be set to a valid GitHub Personal Access Token with the appropriate scopes. For more information see https://docs.github.com/en/migrations/using-github-enterprise-importer/preparing-to-migrate-with-github-enterprise-importer/managing-access-for-github-enterprise-importer#creating-a-personal-access-token-for-github-enterprise-importer"
exit 1
}
else {
Write-Host "GH_PAT environment variable is set and will be used to authenticate to GitHub."
}
# =========== Organization: XXX===========
# === Team Project: XXX/JJL Test ===
Exec { gh ado2gh create-team --target-api-url "https://api.XXX.ghe.com" --github-org "TestMigration3" --team-name "JJL-Test-Maintainers" }
Exec { gh ado2gh create-team --target-api-url "https://api.XXX.ghe.com" --github-org "TestMigration3" --team-name "JJL-Test-Admins" }
Exec { gh ado2gh migrate-repo --target-api-url "https://api.XXX.ghe.com" --ado-org "XXX" --ado-team-project "JJL Test" --ado-repo "JJL Test" --github-org "TestMigration3" --github-repo "JJL-Test-JJL-Test" --target-repo-visibility private }
Exec { gh ado2gh add-team-to-repo --github-org "TestMigration3" --github-repo "JJL-Test-JJL-Test" --team "JJL-Test-Maintainers" --role "maintain" }
Exec { gh ado2gh add-team-to-repo --github-org "TestMigration3" --github-repo "JJL-Test-JJL-Test" --team "JJL-Test-Admins" --role "admin" }
```
It fails on the gh ado2gh add-team-to-repo step.
When I try directly the failing command (gh ado2gh add-team-to-repo) with --verbose, I get this output:
```powershell
gh ado2gh add-team-to-repo --github-org "TestMigration3" --github-repo "JJL-Test-JJL-Test" --team "JJL-Test-Maintainers" --role "maintain" --verbose
[2025-07-28 08:02:30] [INFO] You are running an up-to-date version of the ado2gh CLI [v1.17.0]
[2025-07-28 08:02:30] [INFO] GITHUB ORG: TestMigration3
[2025-07-28 08:02:30] [INFO] GITHUB REPO: JJL-Test-JJL-Test
[2025-07-28 08:02:30] [INFO] TEAM: JJL-Test-Maintainers
[2025-07-28 08:02:30] [INFO] ROLE: maintain
[2025-07-28 08:02:30] [INFO] VERBOSE: true
[2025-07-28 08:02:30] [INFO] Adding team to repo...
[2025-07-28 08:02:30] [DEBUG] HTTP GET: https://api.github.com/orgs/TestMigration3/teams
[2025-07-28 08:02:30] [DEBUG] GITHUB REQUEST ID: DAA0:E89A9:7825072:7C8FE6C:68871276
[2025-07-28 08:02:30] [DEBUG] RESPONSE (Unauthorized): {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest","status":"401"}
[2025-07-28 08:02:30] [DEBUG] [HTTP ERROR 401] System.Net.Http.HttpRequestException: GitHub API error: {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest","status":"401"}
---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at OctoshiftCLI.Services.GithubClient.SendAsync(HttpMethod httpMethod, String url, Object body, HttpStatusCode expectedStatus, Dictionary`2 customHeaders)
--- End of inner exception stack trace ---
at OctoshiftCLI.Services.GithubClient.SendAsync(HttpMethod httpMethod, String url, Object body, HttpStatusCode expectedStatus, Dictionary`2 customHeaders)
at OctoshiftCLI.Services.GithubClient.<>c__DisplayClass20_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
[2025-07-28 08:02:30] [ERROR] OctoshiftCLI.OctoshiftCliException: Unauthorized. Please check your token and try again
---> System.Net.Http.HttpRequestException: GitHub API error: {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest","status":"401"}
---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at OctoshiftCLI.Services.GithubClient.SendAsync(HttpMethod httpMethod, String url, Object body, HttpStatusCode expectedStatus, Dictionary`2 customHeaders)
--- End of inner exception stack trace ---
at OctoshiftCLI.Services.GithubClient.SendAsync(HttpMethod httpMethod, String url, Object body, HttpStatusCode expectedStatus, Dictionary`2 customHeaders)
at OctoshiftCLI.Services.GithubClient.<>c__DisplayClass20_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
--- End of inner exception stack trace ---
at OctoshiftCLI.RetryPolicy.b__8_1[TException](Exception ex, TimeSpan ts, Context ctx)
at Polly.AsyncRetrySyntax.<>c__DisplayClass22_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
at Polly.AsyncPolicy.ExecuteAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext)
at OctoshiftCLI.RetryPolicy.Retry[T](Func`1 func)
at OctoshiftCLI.Services.GithubClient.GetWithRetry(String url, Dictionary`2 customHeaders, HttpStatusCode expectedStatus)
at OctoshiftCLI.Services.GithubClient.GetAllAsync(String url, Func`2 resultCollectionSelector, Dictionary`2 customHeaders)+MoveNext()
at OctoshiftCLI.Services.GithubClient.GetAllAsync(String url, Func`2 resultCollectionSelector, Dictionary`2 customHeaders)+System.Threading.Tasks.Sources.IValueTaskSource.GetResult()
at System.Linq.AsyncEnumerable.g__Core|335_0[TSource](IAsyncEnumerable`1 source, Func`2 predicate, CancellationToken cancellationToken) in /_/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs:line 82
at System.Linq.AsyncEnumerable.g__Core|335_0[TSource](IAsyncEnumerable`1 source, Func`2 predicate, CancellationToken cancellationToken) in /_/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs:line 82
at OctoshiftCLI.Services.GithubApi.GetTeamSlug(String org, String teamName)
at OctoshiftCLI.AdoToGithub.Commands.AddTeamToRepo.AddTeamToRepoCommandHandler.Handle(AddTeamToRepoCommandArgs args)
at OctoshiftCLI.Extensions.CommandExtensions.RunHandler[TArgs,THandler](TArgs args, ServiceProvider sp, CommandBase`2 command)
at OctoshiftCLI.Extensions.CommandExtensions.<>c__DisplayClass1_0`3.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Invocation.AnonymousCommandHandler.InvokeAsync(InvocationContext)
at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass12_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass22_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass19_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<b__18_0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass16_0.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<b__5_0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass8_0.<b__0>d.MoveNext()
```
The token has these permissions:
Based on the verbose output, it seems that this command doesn't use the --target-api-url parameter and is calling the api.github.com instead api.xxx.ghe.com. It is needed to extend it in same way as done in
#1365
Contributor guide
Assessment
This issue has not been assessed yet.