From 9fd6cfa998f6b6f4f645cfba477d8b4e48f70380 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 10:27:39 +0800 Subject: [PATCH 1/9] Ignore stale init prompts during sample download --- .../tests/e2e-live/console_test.go | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index d478f358081..31f8dec1953 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -194,12 +194,42 @@ func activePrompt(screen string) string { lines := nonEmptyLines(screen) for i := len(lines) - 1; i >= 0; i-- { if strings.HasPrefix(lines[i], "?") { + if hasInitProgressAfterPrompt(lines[i+1:]) { + return "" + } return strings.ToLower(lines[i]) } } return "" } +// isInitProgressLine reports whether line is progress emitted after a prompt was +// already answered. Survey leaves answered prompts on screen while the extension +// downloads/copies samples; those stale prompts should not be treated as active. +func isInitProgressLine(line string) bool { + line = strings.ToLower(line) + return strings.Contains(line, "downloading sample from github") || + strings.HasPrefix(line, "agents.md") || + strings.HasPrefix(line, "claude.md") || + strings.HasPrefix(line, "readme.md") || + strings.HasPrefix(line, "azure.yaml") || + strings.HasPrefix(line, "src/") || + strings.Contains(line, "setting up github connection") || + strings.Contains(line, "adopting the sample's azure.yaml") || + strings.Contains(line, "initializing an app to run on azure") || + strings.Contains(line, "copying template code from local path") || + strings.Contains(line, "installing required extensions") +} + +func hasInitProgressAfterPrompt(lines []string) bool { + for _, line := range lines { + if isInitProgressLine(line) { + return true + } + } + return false +} + // screenContains reports whether screen contains sub (case-insensitive). func screenContains(screen, sub string) bool { return strings.Contains(strings.ToLower(screen), strings.ToLower(sub)) From d90ffe8e7a31dcbdc09c54dca0609de837859a49 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 11:00:01 +0800 Subject: [PATCH 2/9] test: apply go fix for stale init prompt helper --- .../azure.ai.agents/tests/e2e-live/console_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index 31f8dec1953..7a9b7d3a1cc 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -8,6 +8,7 @@ package e2elive import ( "fmt" "os" + "slices" "strings" "sync" "time" @@ -222,12 +223,7 @@ func isInitProgressLine(line string) bool { } func hasInitProgressAfterPrompt(lines []string) bool { - for _, line := range lines { - if isInitProgressLine(line) { - return true - } - } - return false + return slices.ContainsFunc(lines, isInitProgressLine) } // screenContains reports whether screen contains sub (case-insensitive). From 2d1c7002cd68ae5cf204684193dde3fd421efa0d Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 11:42:05 +0800 Subject: [PATCH 3/9] test: ignore answered template prompts without choices --- .../tests/e2e-live/console_test.go | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index 7a9b7d3a1cc..1a3b262db18 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -11,6 +11,7 @@ import ( "slices" "strings" "sync" + "testing" "time" expect "github.com/Netflix/go-expect" @@ -195,7 +196,8 @@ func activePrompt(screen string) string { lines := nonEmptyLines(screen) for i := len(lines) - 1; i >= 0; i-- { if strings.HasPrefix(lines[i], "?") { - if hasInitProgressAfterPrompt(lines[i+1:]) { + after := lines[i+1:] + if hasInitProgressAfterPrompt(after) || isAnsweredTemplatePrompt(lines[i], after) { return "" } return strings.ToLower(lines[i]) @@ -226,7 +228,45 @@ func hasInitProgressAfterPrompt(lines []string) bool { return slices.ContainsFunc(lines, isInitProgressLine) } +func isAnsweredTemplatePrompt(line string, after []string) bool { + line = strings.ToLower(line) + if !strings.Contains(line, "starter template") && !strings.Contains(line, "agent template") { + return false + } + if !strings.Contains(line, ":") { + return false + } + return !slices.ContainsFunc(after, isSurveyChoiceLine) +} + +func isSurveyChoiceLine(line string) bool { + return strings.HasPrefix(strings.TrimSpace(line), ">") +} + // screenContains reports whether screen contains sub (case-insensitive). func screenContains(screen, sub string) bool { return strings.Contains(strings.ToLower(screen), strings.ToLower(sub)) } + +func TestActivePromptIgnoresAnsweredSelectWithoutChoices(t *testing.T) { + screen := strings.Join([]string{ + "? Select a language: Python", + "? Select a starter template: Basic agent (Invocations, Agent Framework, Python)", + }, "\n") + + if got := activePrompt(screen); got != "" { + t.Fatalf("activePrompt() = %q, want empty", got) + } +} + +func TestActivePromptKeepsActiveSelectWithChoices(t *testing.T) { + screen := strings.Join([]string{ + "? Select a starter template: Basic agent (Invocations, Agent Framework, Python)", + "> Basic agent (Invocations, Agent Framework, Python)", + }, "\n") + + want := "? select a starter template: basic agent (invocations, agent framework, python)" + if got := activePrompt(screen); got != want { + t.Fatalf("activePrompt() = %q, want %q", got, want) + } +} From 78e72ca7f03650d0932854d5a9340784c99a1fbf Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 14:10:35 +0800 Subject: [PATCH 4/9] test: fail fast on GitHub CLI auth prompts --- .../tests/e2e-live/console_test.go | 22 +++++++++ .../tests/e2e-live/tier2_live_test.go | 49 +++++++++---------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index 1a3b262db18..eee0fef4ef9 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -270,3 +270,25 @@ func TestActivePromptKeepsActiveSelectWithChoices(t *testing.T) { t.Fatalf("activePrompt() = %q, want %q", got, want) } } + +func TestIsGitHubAuthPrompt(t *testing.T) { + cases := []struct { + name string + prompt string + want bool + }{ + {"git protocol", "? what is your preferred protocol for git operations on this host?", true}, + {"git credentials", "? authenticate git with your github credentials? (y/n)", true}, + {"browser auth", "? how would you like to authenticate github cli? login with a web browser", true}, + {"device login", "Press Enter to open https://github.com/login/device", true}, + {"regular prompt", "? select a starter template: basic agent", false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := isGitHubAuthPrompt(tc.prompt); got != tc.want { + t.Fatalf("isGitHubAuthPrompt(%q) = %v, want %v", tc.prompt, got, tc.want) + } + }) + } +} diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go index b592831a8eb..75704276b2c 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go @@ -221,24 +221,18 @@ func (r *runner) run(ctx context.Context) { } } -// phaseInitWithRetry retries once when a bad GitHub token from the pipeline -// environment causes gh to fail before the public sample is downloaded. The -// retry drops token override env vars so gh can use any ambient auth state. +// phaseInitWithRetry keeps the init failure mode explicit when a bad GitHub +// token from the pipeline environment causes gh to fail before the public sample +// is downloaded. Retrying without the token is unsafe in CI: gh can fall back to +// an interactive browser/device-login flow that the PTY driver would otherwise +// keep answering. func (r *runner) phaseInitWithRetry(ctx context.Context) error { err := r.phaseInit(ctx) if err == nil || !isInvalidGitHubTokenError(err) { return err } - r.t.Log("init failed because GH_TOKEN/GITHUB_TOKEN is invalid; retrying without token override env vars") - r.env = withoutGitHubTokenEnv(r.env) - if err := os.RemoveAll(r.testDir); err != nil { - return fmt.Errorf("reset test dir after GitHub token failure: %w", err) - } - if err := os.MkdirAll(r.testDir, 0o700); err != nil { - return fmt.Errorf("recreate test dir after GitHub token failure: %w", err) - } - return r.phaseInit(ctx) + return fmt.Errorf("init failed because GH_TOKEN/GITHUB_TOKEN is invalid; refusing to retry without token because gh can prompt for interactive authentication in CI: %w", err) } // phaseInit runs `azd ai agent init` attached to a pseudo-terminal and drives @@ -375,7 +369,9 @@ func (r *runner) driveInit(ctx context.Context, exited <-chan struct{}) error { } } - r.dispatchPrompt(screen, prompt) + if err := r.dispatchPrompt(screen, prompt); err != nil { + return err + } } } @@ -422,10 +418,13 @@ func promptKey(prompt string) string { // model, deployment name, capacity/sku/version). The rest are kept as defensive // handlers because init auto-resolves them under userProvidedManifest=true (so // they normally do NOT prompt) or only surfaces them for specific runtime state. -func (r *runner) dispatchPrompt(screen, prompt string) { +func (r *runner) dispatchPrompt(screen, prompt string) error { has := func(sub string) bool { return strings.Contains(prompt, sub) } switch { + case isGitHubAuthPrompt(prompt): + return fmt.Errorf("init reached interactive GitHub CLI authentication prompt: %q; check GH_TOKEN/GITHUB_TOKEN for the live pipeline", prompt) + // Yes/No confirms. "Continue with this existing agent name?" // (resolveExistingAgentNameConflictWithChecker) only fires when the unique // name already exists; decline it to reach the fresh-name input. Any other @@ -541,6 +540,17 @@ func (r *runner) dispatchPrompt(screen, prompt string) { r.t.Logf("unhandled prompt (default Enter): %s", truncate(prompt, 100)) r.enter() } + + return nil +} + +func isGitHubAuthPrompt(prompt string) bool { + prompt = strings.ToLower(prompt) + return strings.Contains(prompt, "preferred protocol for git operations") || + strings.Contains(prompt, "authenticate git with your github credentials") || + strings.Contains(prompt, "authenticate github cli") || + strings.Contains(prompt, "login with a web browser") || + strings.Contains(prompt, "github.com/login/device") } // phaseProvision finds the scaffolded project and runs `azd provision`. @@ -1156,17 +1166,6 @@ func isInvalidGitHubTokenError(err error) bool { strings.Contains(msg, "The token in GITHUB_TOKEN is invalid") } -func withoutGitHubTokenEnv(env []string) []string { - out := env[:0] - for _, kv := range env { - if strings.HasPrefix(kv, "GH_TOKEN=") || strings.HasPrefix(kv, "GITHUB_TOKEN=") { - continue - } - out = append(out, kv) - } - return out -} - // shortHash returns a short, non-cryptographic uniqueness suffix for the agent // name (sha256 only to avoid noise from security scanners). func shortHash(mode string) string { From 761f9e76b6142284e16e5280afe6f7c0e9f87884 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 16:26:17 +0800 Subject: [PATCH 5/9] test: address AI agents live review comments --- .../tests/e2e-live/console_test.go | 33 +++++++++++++++++-- .../tests/e2e-live/tier2_live_test.go | 18 +++++++--- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index eee0fef4ef9..9b4241b2c98 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -189,9 +189,10 @@ func nonEmptyLines(screen string) []string { return out } -// activePrompt returns the lowercased text of the last survey "?" prompt line on -// screen, or "" if none is visible. The last "?" line is the one survey is -// currently blocking on (earlier "?" lines are answered prompts it echoed). +// activePrompt returns the lowercased text of the last active survey "?" prompt +// line on screen, or "" if none is visible. Survey leaves answered prompts on +// screen, so a prompt followed by init progress or an answered template selection +// without choice rows is treated as stale. func activePrompt(screen string) string { lines := nonEmptyLines(screen) for i := len(lines) - 1; i >= 0; i-- { @@ -259,6 +260,32 @@ func TestActivePromptIgnoresAnsweredSelectWithoutChoices(t *testing.T) { } } +func TestActivePromptIgnoresPromptFollowedByInitProgress(t *testing.T) { + screen := strings.Join([]string{ + "? Select a language: Python", + "Downloading sample from GitHub: Azure-Samples/azure-ai-agent-service-enterprise-demo", + "azure.yaml", + }, "\n") + + if got := activePrompt(screen); got != "" { + t.Fatalf("activePrompt() = %q, want empty", got) + } +} + +func TestActivePromptKeepsLaterRealPromptAfterProgress(t *testing.T) { + screen := strings.Join([]string{ + "? Select a language: Python", + "Downloading sample from GitHub: Azure-Samples/azure-ai-agent-service-enterprise-demo", + "? Select an Azure subscription: Contoso Test Subscription", + "> Contoso Test Subscription", + }, "\n") + + want := "? select an azure subscription: contoso test subscription" + if got := activePrompt(screen); got != want { + t.Fatalf("activePrompt() = %q, want %q", got, want) + } +} + func TestActivePromptKeepsActiveSelectWithChoices(t *testing.T) { screen := strings.Join([]string{ "? Select a starter template: Basic agent (Invocations, Agent Framework, Python)", diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go index 75704276b2c..976003bfd8c 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go @@ -195,7 +195,7 @@ func setupConfigDir(t *testing.T, configDir string) { // this is the fast way to iterate on the provision/down path (issue #8839) // without paying for a full agent deploy. func (r *runner) run(ctx context.Context) { - if err := r.phaseInitWithRetry(ctx); err != nil { + if err := r.phaseInitWithTokenCheck(ctx); err != nil { r.t.Errorf("init: %v", err) return } @@ -221,18 +221,22 @@ func (r *runner) run(ctx context.Context) { } } -// phaseInitWithRetry keeps the init failure mode explicit when a bad GitHub +// phaseInitWithTokenCheck keeps the init failure mode explicit when a bad GitHub // token from the pipeline environment causes gh to fail before the public sample // is downloaded. Retrying without the token is unsafe in CI: gh can fall back to // an interactive browser/device-login flow that the PTY driver would otherwise // keep answering. -func (r *runner) phaseInitWithRetry(ctx context.Context) error { +func (r *runner) phaseInitWithTokenCheck(ctx context.Context) error { err := r.phaseInit(ctx) if err == nil || !isInvalidGitHubTokenError(err) { return err } - return fmt.Errorf("init failed because GH_TOKEN/GITHUB_TOKEN is invalid; refusing to retry without token because gh can prompt for interactive authentication in CI: %w", err) + return fmt.Errorf( + "init failed because GH_TOKEN/GITHUB_TOKEN is invalid; refusing to retry without token "+ + "because gh can prompt for interactive authentication in CI: %w", + err, + ) } // phaseInit runs `azd ai agent init` attached to a pseudo-terminal and drives @@ -423,7 +427,11 @@ func (r *runner) dispatchPrompt(screen, prompt string) error { switch { case isGitHubAuthPrompt(prompt): - return fmt.Errorf("init reached interactive GitHub CLI authentication prompt: %q; check GH_TOKEN/GITHUB_TOKEN for the live pipeline", prompt) + return fmt.Errorf( + "init reached interactive GitHub CLI authentication prompt: %q; "+ + "check GH_TOKEN/GITHUB_TOKEN for the live pipeline", + prompt, + ) // Yes/No confirms. "Continue with this existing agent name?" // (resolveExistingAgentNameConflictWithChecker) only fires when the unique From 4306afe0311b27a7d803877bcaa323f2ce93420f Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 16:33:11 +0800 Subject: [PATCH 6/9] test: detect GitHub device login prompt --- .../tests/e2e-live/console_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go index 9b4241b2c98..c384c5f807e 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/console_test.go @@ -196,6 +196,9 @@ func nonEmptyLines(screen string) []string { func activePrompt(screen string) string { lines := nonEmptyLines(screen) for i := len(lines) - 1; i >= 0; i-- { + if isGitHubAuthPrompt(lines[i]) { + return strings.ToLower(lines[i]) + } if strings.HasPrefix(lines[i], "?") { after := lines[i+1:] if hasInitProgressAfterPrompt(after) || isAnsweredTemplatePrompt(lines[i], after) { @@ -319,3 +322,15 @@ func TestIsGitHubAuthPrompt(t *testing.T) { }) } } + +func TestActivePromptDetectsGitHubDeviceLoginLine(t *testing.T) { + screen := strings.Join([]string{ + "? how would you like to authenticate github cli? login with a web browser", + "Press Enter to open https://github.com/login/device", + }, "\n") + + want := "press enter to open https://github.com/login/device" + if got := activePrompt(screen); got != want { + t.Fatalf("activePrompt() = %q, want %q", got, want) + } +} From bf3d1de5db5e500a2f7fd4e4564b59fef5328793 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 17:16:26 +0800 Subject: [PATCH 7/9] test: ignore unusable GitHub tokens in live driver --- .../tests/e2e-live/tier2_live_test.go | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go index 976003bfd8c..6e14a9fa917 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go @@ -127,7 +127,7 @@ func newRunner(t *testing.T, mode string) *runner { configDir := filepath.Join(os.TempDir(), "e2e-azd-config-"+mode) setupConfigDir(t, configDir) - env := os.Environ() + env := withoutGitHubTokenEnv(os.Environ()) env = append(env, "AZD_CONFIG_DIR="+configDir) if tenant := os.Getenv("E2E_TENANT"); tenant != "" { env = append(env, "AZURE_TENANT_ID="+tenant) @@ -1151,29 +1151,54 @@ func exitCode(err error) int { return -1 } -// ghToken resolves a GitHub token from the environment, falling back to `gh`. +// ghToken resolves a usable GitHub token from the environment, falling back to `gh`. func ghToken() string { for _, k := range []string{"GITHUB_TOKEN", "GH_TOKEN"} { if v := os.Getenv(k); v != "" { - return v + if isUsableGitHubToken(v) { + return v + } + return "" } } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() //nolint:gosec // gh is a trusted fixed binary; no user input in args. - out, err := exec.CommandContext(ctx, "gh", "auth", "token").Output() + cmd := exec.CommandContext(ctx, "gh", "auth", "token") + cmd.Env = withoutGitHubTokenEnv(os.Environ()) + out, err := cmd.Output() if err != nil { return "" } return strings.TrimSpace(string(out)) } +func isUsableGitHubToken(token string) bool { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + //nolint:gosec // gh is a trusted fixed binary; no user input in args. + cmd := exec.CommandContext(ctx, "gh", "auth", "status", "--hostname", "github.com") + cmd.Env = append(withoutGitHubTokenEnv(os.Environ()), "GH_TOKEN="+token, "GITHUB_TOKEN="+token) + return cmd.Run() == nil +} + func isInvalidGitHubTokenError(err error) bool { msg := err.Error() return strings.Contains(msg, "The token in GH_TOKEN is invalid") || strings.Contains(msg, "The token in GITHUB_TOKEN is invalid") } +func withoutGitHubTokenEnv(env []string) []string { + out := make([]string, 0, len(env)) + for _, kv := range env { + if strings.HasPrefix(kv, "GH_TOKEN=") || strings.HasPrefix(kv, "GITHUB_TOKEN=") { + continue + } + out = append(out, kv) + } + return out +} + // shortHash returns a short, non-cryptographic uniqueness suffix for the agent // name (sha256 only to avoid noise from security scanners). func shortHash(mode string) string { From bcfb34e80eef586868ae41fdd4c475a48e0a0f15 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 19:18:15 +0800 Subject: [PATCH 8/9] test: continue past invalid GitHub token candidates --- .../extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go index 6e14a9fa917..4ed81ffb549 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go @@ -1158,7 +1158,6 @@ func ghToken() string { if isUsableGitHubToken(v) { return v } - return "" } } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) From 77a867d436ebbbc06eadb76929926b44beba9813 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Mon, 13 Jul 2026 19:28:50 +0800 Subject: [PATCH 9/9] test: validate stored GitHub token fallback --- .../azure.ai.agents/tests/e2e-live/tier2_live_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go index 4ed81ffb549..4a187f3e555 100644 --- a/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go +++ b/cli/azd/extensions/azure.ai.agents/tests/e2e-live/tier2_live_test.go @@ -1169,7 +1169,11 @@ func ghToken() string { if err != nil { return "" } - return strings.TrimSpace(string(out)) + token := strings.TrimSpace(string(out)) + if token == "" || !isUsableGitHubToken(token) { + return "" + } + return token } func isUsableGitHubToken(token string) bool {