-
Notifications
You must be signed in to change notification settings - Fork 429
Improve Copilot 403 auth guidance for copilot-requests mode #40052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41add2d
e9c0747
ef58ece
23bce86
fdb7934
30d839e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ const { | |
| buildMissingToolAlternatives, | ||
| buildInfrastructureIncompletePayload, | ||
| buildCopilotProxyAuthFailureDiagnostic, | ||
| envFlagEnabled, | ||
| buildPromptFileFallbackInstruction, | ||
| countPermissionDeniedIssues, | ||
| detectCopilotErrors, | ||
|
|
@@ -972,6 +973,59 @@ describe("copilot_harness.cjs", () => { | |
| expect(diagnostic).not.toContain("COPILOT_PROVIDER_API_KEY"); | ||
| }); | ||
|
|
||
| it("rewrites local proxy 403 errors in copilot-requests mode to org-billing guidance", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Authentication failed with provider at http://172.30.0.30:10002 (HTTP 403).\nCheck your COPILOT_PROVIDER_API_KEY or COPILOT_PROVIDER_BEARER_TOKEN.", { | ||
| COPILOT_MODEL: "claude-sonnet-4.5", | ||
| S2STOKENS: "true", | ||
| }); | ||
|
|
||
| expect(diagnostic).toContain("Copilot requests authentication failed"); | ||
| expect(diagnostic).toContain("HTTP 403"); | ||
| expect(diagnostic).toContain("model=claude-sonnet-4.5"); | ||
| expect(diagnostic).toContain("stage=starting the Copilot CLI request"); | ||
| expect(diagnostic).toContain("permissions.copilot-requests: write"); | ||
| expect(diagnostic).toContain("centralized Copilot billing"); | ||
| expect(diagnostic).toContain("https://github.github.com/gh-aw/reference/billing/"); | ||
| expect(diagnostic).not.toContain("COPILOT_PROVIDER_API_KEY"); | ||
| }); | ||
|
pelikhan marked this conversation as resolved.
|
||
|
|
||
| it("treats truthy S2STOKENS values as copilot-requests mode for 403 guidance", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Authentication failed with provider at http://172.30.0.30:10002 (HTTP 403).", { | ||
| COPILOT_MODEL: "claude-sonnet-4.5", | ||
| S2STOKENS: " YES ", | ||
| }); | ||
|
|
||
| expect(diagnostic).toContain("Copilot requests authentication failed"); | ||
|
pelikhan marked this conversation as resolved.
|
||
| expect(diagnostic).toContain("https://github.github.com/gh-aw/reference/billing/"); | ||
| expect(diagnostic).not.toContain("COPILOT_PROVIDER_API_KEY"); | ||
| }); | ||
|
|
||
| it("returns empty string for proxy 403 when S2STOKENS is not set (BYOK mode)", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Authentication failed with provider at http://172.30.0.30:10002 (HTTP 403).", { | ||
| COPILOT_MODEL: "claude-sonnet-4.5", | ||
| }); | ||
|
|
||
| expect(diagnostic).toBe(""); | ||
| }); | ||
|
|
||
| it("returns empty string for proxy 403 when S2STOKENS is falsy", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Authentication failed with provider at http://172.30.0.30:10002 (HTTP 403).", { | ||
| COPILOT_MODEL: "claude-sonnet-4.5", | ||
| S2STOKENS: "false", | ||
| }); | ||
|
|
||
| expect(diagnostic).toBe(""); | ||
| }); | ||
|
|
||
| it("returns empty string for non-proxy 403 even when S2STOKENS is true", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Authentication failed with provider at (api.anthropic.com/redacted) (HTTP 403).", { | ||
| COPILOT_MODEL: "claude-sonnet-4.5", | ||
| S2STOKENS: "true", | ||
| }); | ||
|
|
||
| expect(diagnostic).toBe(""); | ||
| }); | ||
|
pelikhan marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Falsy S2STOKENS and non-proxy-URL paths are completely untested: the test suite covers only truthy values, leaving two correctness gaps that would hide regressions. 💡 Suggested additionsGap 1 — falsy S2STOKENS returns empty string (S2STOKENS gate correctness): it("returns empty string for proxy 403 when S2STOKENS is not set", () => {
const diagnostic = buildCopilotProxyAuthFailureDiagnostic(
"Authentication failed with provider at (172.30.0.30/redacted) (HTTP 403).",
{ COPILOT_MODEL: "claude-sonnet-4.5" } // no S2STOKENS
);
expect(diagnostic).toBe("");
});Gap 2 — non-proxy URL with S2STOKENS=true returns empty string (proxy-URL gate correctness): it("returns empty string for non-proxy 403 even when S2STOKENS is true", () => {
const diagnostic = buildCopilotProxyAuthFailureDiagnostic(
"Authentication failed with provider at (api.anthropic.com/redacted) (HTTP 403).",
{ COPILOT_MODEL: "claude-sonnet-4.5", S2STOKENS: "true" }
);
expect(diagnostic).toBe("");
});Without these, accidentally inverting the |
||
|
|
||
| it("reports token-validation stage when present in the output", () => { | ||
| const diagnostic = buildCopilotProxyAuthFailureDiagnostic("Validating token with provider.\nAuthentication failed with provider at http://localhost:10002 (HTTP 401).", { COPILOT_MODEL: "gpt-4.1" }); | ||
|
|
||
|
|
@@ -997,6 +1051,20 @@ describe("copilot_harness.cjs", () => { | |
| }); | ||
| }); | ||
|
|
||
| describe("envFlagEnabled", () => { | ||
| it.each(["true", "TRUE", "True", "1", "yes", " YES "])("returns true for '%s'", v => { | ||
| expect(envFlagEnabled(v)).toBe(true); | ||
| }); | ||
|
|
||
| it.each(["false", "FALSE", "0", "no", "", " "])("returns false for '%s'", v => { | ||
| expect(envFlagEnabled(v)).toBe(false); | ||
| }); | ||
|
|
||
| it("returns false for undefined", () => { | ||
| expect(envFlagEnabled(undefined)).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe("auth error prevents retry", () => { | ||
| // Inline the same retry logic as the driver, including auth error check | ||
| const MCP_POLICY_BLOCKED_PATTERN = /MCP servers were blocked by policy:/; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Copilot requests authentication failed through the gh-aw API proxy (HTTP 403, model={selected_model}, stage={stage}). This workflow is using permissions.copilot-requests: write, so Copilot requests must be allowed through your organization's centralized Copilot billing configuration. Verify that copilot-requests: write is granted to the workflow or job and that Copilot org billing is enabled for your organization. See https://github.github.com/gh-aw/reference/billing/ for details. |
Uh oh!
There was an error while loading. Please reload this page.