Summary
Some GitHub orgs enforce a policy blocking classic personal access tokens (Settings → Personal access tokens → restrict access via PAT type), requiring a fine-grained PAT, GitHub App, or OAuth App token instead. This is not a fullsend requirement — most orgs work fine with whatever token gh auth login produces. It's org-specific policy, and today neither the CLI nor the docs help a user who hits it.
When fullsend github setup <org>/<repo> runs against such an org, it fails with the raw GitHub API error surfaced verbatim:
Error: getting repo info: get repo <org>/<repo>: github api: 403 `<org>` forbids access via a
personal access token (classic). Please use a GitHub App, OAuth App, or a personal access token
with fine-grained permissions.
This has two separate gaps:
Gap 1: The CLI doesn't explain why or what to do
applyPerRepoScaffold (internal/cli/admin.go:1181-1184) and other GetRepo call sites just wrap the raw error:
targetRepo, err := client.GetRepo(ctx, owner, repo)
if err != nil {
return fmt.Errorf("getting repo info: %w", err)
}
The underlying APIError (internal/forge/github/github.go) already carries StatusCode and Message, and the codebase has an existing pattern for classifying specific API error conditions into actionable sentinels/messages (see IsRateLimitError, ErrNotFound, ErrAlreadyExists in the same file). This 403 case isn't one of them, so it just bubbles up as an opaque GitHub API message with no fullsend-specific remediation.
Ask: detect this specific 403 (status 403 + message containing "forbids access via a personal access token") and print fullsend-specific guidance instead of (or in addition to) the raw API message — e.g.:
- Explain that
resolveToken() (internal/cli/admin.go:71-86) checks GH_TOKEN → GITHUB_TOKEN → gh auth token, in that order, so whichever of those currently resolves is the token that got rejected.
- Explain that a fine-grained PAT must be created manually via the GitHub web UI (no
gh command creates one) and exported as GH_TOKEN to take priority.
- Link to the specific repository permissions needed (see Gap 2).
Gap 2: Docs don't mention token resolution or fine-grained requirements at all
docs/guides/getting-started/configuring-github.md's Prerequisites section only says:
Download the latest gh CLI and authenticate with it.
A user who runs gh auth login and then hits an org with this policy has no doc pointer to GH_TOKEN/GITHUB_TOKEN, no mention that gh auth login's default OAuth-App token is a different credential class from a fine-grained PAT, and no list of which repository permissions to grant.
Suggested permissions to document, based on what runGitHubSetupPerRepo actually calls (internal/forge/github/github.go):
| Permission |
Level |
Why |
| Contents |
Read and write |
Commits .fullsend/config.yaml and scaffold files |
| Workflows |
Read and write |
Writes/updates files under .github/workflows/ |
| Secrets |
Read and write |
Sets FULLSEND_GCP_PROJECT_ID / FULLSEND_GCP_WIF_PROVIDER |
| Variables |
Read and write |
Sets FULLSEND_MINT_URL / FULLSEND_GCP_REGION |
| Metadata |
Read-only |
GitHub-required baseline |
| Pull requests |
Read and write |
Only needed without --direct |
Suggested doc addition to the Prerequisites section:
If your organization restricts personal access token types, gh auth login may not produce a
token accepted by the GitHub API. Generate a fine-grained personal access token manually
(Contents, Workflows, Secrets, Variables, Pull requests: read/write) and export it as
GH_TOKEN before running fullsend github setup — it takes priority over gh auth token.
Impact
Users on orgs with PAT restrictions hit an opaque 403 with no CLI-level explanation and no doc guidance on how the CLI resolves credentials or what permissions to grant.
Summary
Some GitHub orgs enforce a policy blocking classic personal access tokens (Settings → Personal access tokens → restrict access via PAT type), requiring a fine-grained PAT, GitHub App, or OAuth App token instead. This is not a fullsend requirement — most orgs work fine with whatever token
gh auth loginproduces. It's org-specific policy, and today neither the CLI nor the docs help a user who hits it.When
fullsend github setup <org>/<repo>runs against such an org, it fails with the raw GitHub API error surfaced verbatim:This has two separate gaps:
Gap 1: The CLI doesn't explain why or what to do
applyPerRepoScaffold(internal/cli/admin.go:1181-1184) and otherGetRepocall sites just wrap the raw error:The underlying
APIError(internal/forge/github/github.go) already carriesStatusCodeandMessage, and the codebase has an existing pattern for classifying specific API error conditions into actionable sentinels/messages (seeIsRateLimitError,ErrNotFound,ErrAlreadyExistsin the same file). This 403 case isn't one of them, so it just bubbles up as an opaque GitHub API message with no fullsend-specific remediation.Ask: detect this specific 403 (status 403 + message containing "forbids access via a personal access token") and print fullsend-specific guidance instead of (or in addition to) the raw API message — e.g.:
resolveToken()(internal/cli/admin.go:71-86) checksGH_TOKEN→GITHUB_TOKEN→gh auth token, in that order, so whichever of those currently resolves is the token that got rejected.ghcommand creates one) and exported asGH_TOKENto take priority.Gap 2: Docs don't mention token resolution or fine-grained requirements at all
docs/guides/getting-started/configuring-github.md's Prerequisites section only says:A user who runs
gh auth loginand then hits an org with this policy has no doc pointer toGH_TOKEN/GITHUB_TOKEN, no mention thatgh auth login's default OAuth-App token is a different credential class from a fine-grained PAT, and no list of which repository permissions to grant.Suggested permissions to document, based on what
runGitHubSetupPerRepoactually calls (internal/forge/github/github.go):.fullsend/config.yamland scaffold files.github/workflows/FULLSEND_GCP_PROJECT_ID/FULLSEND_GCP_WIF_PROVIDERFULLSEND_MINT_URL/FULLSEND_GCP_REGION--directSuggested doc addition to the Prerequisites section:
Impact
Users on orgs with PAT restrictions hit an opaque 403 with no CLI-level explanation and no doc guidance on how the CLI resolves credentials or what permissions to grant.