From 055f67bcf294409383cb08b601904915fa950284 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Sun, 31 May 2026 13:36:03 -0700 Subject: [PATCH 1/3] fix: make env refresh tolerant of unsupported service hosts When extension-provided service hosts (like azure.ai.agent) are not loaded, env refresh fails with 'service host is unsupported'. Since env refresh only needs infrastructure outputs from Azure (not service targets), it should gracefully skip unsupported hosts rather than failing. Add hasUnsupportedHostError() helper that checks the error chain for UnsupportedServiceHostError, and use it in envRefreshAction.Run() to continue past Initialize and EnsureAllTools failures caused by unresolved extension hosts. Fixes #7195 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/azd/cmd/env.go | 19 +++++++++++-- cli/azd/cmd/env_test.go | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/cli/azd/cmd/env.go b/cli/azd/cmd/env.go index 40bd177862f..f1f0458abdb 100644 --- a/cli/azd/cmd/env.go +++ b/cli/azd/cmd/env.go @@ -1151,12 +1151,19 @@ func (ef *envRefreshAction) Run(ctx context.Context) (*actions.ActionResult, err Title: fmt.Sprintf("Refreshing environment %s (azd env refresh)", ef.env.Name()), }) + // Initialize services, skipping any with unsupported hosts (e.g., extension-provided hosts + // that are not currently loaded). Env refresh only needs infrastructure outputs from Azure; + // services with unsupported hosts simply won't receive the environment-updated event. if err := ef.projectManager.Initialize(ctx, ef.projectConfig); err != nil { - return nil, err + if !hasUnsupportedHostError(err) { + return nil, err + } } if err := ef.projectManager.EnsureAllTools(ctx, ef.projectConfig, nil); err != nil { - return nil, err + if !hasUnsupportedHostError(err) { + return nil, err + } } infra, err := ef.importManager.ProjectInfrastructure(ctx, ef.projectConfig) @@ -1253,6 +1260,14 @@ func (ef *envRefreshAction) Run(ctx context.Context) (*actions.ActionResult, err }, nil } +// hasUnsupportedHostError reports whether err (or any error in its chain) is caused by +// an unsupported service host. This lets env refresh continue when extension-provided hosts +// (e.g., azure.ai.agent) are not loaded. +func hasUnsupportedHostError(err error) bool { + _, ok := errors.AsType[*project.UnsupportedServiceHostError](err) + return ok +} + func newEnvGetValuesFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *envGetValuesFlags { flags := &envGetValuesFlags{} flags.Bind(cmd.Flags(), global) diff --git a/cli/azd/cmd/env_test.go b/cli/azd/cmd/env_test.go index e0b5abd24a4..30c1c13897a 100644 --- a/cli/azd/cmd/env_test.go +++ b/cli/azd/cmd/env_test.go @@ -4,8 +4,11 @@ package cmd import ( + "fmt" "testing" + "github.com/azure/azure-dev/cli/azd/internal" + "github.com/azure/azure-dev/cli/azd/pkg/project" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -170,3 +173,59 @@ func TestNewEnvConfigUnsetCmd(t *testing.T) { require.Equal(t, "unset ", cmd.Use) require.NotEmpty(t, cmd.Short) } + +func TestHasUnsupportedHostError(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + err error + expected bool + }{ + { + name: "nil error", + err: nil, + expected: false, + }, + { + name: "unrelated error", + err: assert.AnError, + expected: false, + }, + { + name: "direct UnsupportedServiceHostError", + err: &project.UnsupportedServiceHostError{ + Host: "azure.ai.agent", + ServiceName: "agent", + }, + expected: true, + }, + { + name: "wrapped UnsupportedServiceHostError", + err: fmt.Errorf("initializing service 'agent', %w", &project.UnsupportedServiceHostError{ + Host: "azure.ai.agent", + ServiceName: "agent", + }), + expected: true, + }, + { + name: "wrapped in ErrorWithSuggestion", + err: fmt.Errorf("getting service target: %w", &internal.ErrorWithSuggestion{ + Err: &project.UnsupportedServiceHostError{ + Host: "azure.ai.agent", + ServiceName: "agent", + }, + Suggestion: "install an extension", + }), + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := hasUnsupportedHostError(tt.err) + assert.Equal(t, tt.expected, got) + }) + } +} From 2e365cc42157f88b8f6dac472d6f367d3f13c4a5 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Sun, 31 May 2026 13:59:38 -0700 Subject: [PATCH 2/3] refactor: push unsupported-host tolerance into Initialize/EnsureAllTools Move the UnsupportedServiceHostError handling from the call-site in envRefreshAction into ProjectManager.Initialize and EnsureAllTools themselves. Now both methods continue initializing remaining services after encountering an unsupported host, returning accumulated errors only after processing all services. This ensures that services ordered after an unsupported-host service still get initialized and can receive ServiceEventEnvUpdated events. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/azd/cmd/env_test.go | 15 +++++++++++++++ cli/azd/pkg/project/project_manager.go | 25 +++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cli/azd/cmd/env_test.go b/cli/azd/cmd/env_test.go index 30c1c13897a..bcfd09b1061 100644 --- a/cli/azd/cmd/env_test.go +++ b/cli/azd/cmd/env_test.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "testing" @@ -219,6 +220,20 @@ func TestHasUnsupportedHostError(t *testing.T) { }), expected: true, }, + { + name: "errors.Join with UnsupportedServiceHostError", + err: errors.Join( + fmt.Errorf("initializing service 'agent', %w", &project.UnsupportedServiceHostError{ + Host: "azure.ai.agent", + ServiceName: "agent", + }), + fmt.Errorf("initializing service 'other', %w", &project.UnsupportedServiceHostError{ + Host: "azure.ai.other", + ServiceName: "other", + }), + ), + expected: true, + }, } for _, tt := range tests { diff --git a/cli/azd/pkg/project/project_manager.go b/cli/azd/pkg/project/project_manager.go index 652f41f5722..cfe21808ebf 100644 --- a/cli/azd/pkg/project/project_manager.go +++ b/cli/azd/pkg/project/project_manager.go @@ -92,7 +92,9 @@ func NewProjectManager( } } -// Initializes the project and all child services defined within the project configuration +// Initializes the project and all child services defined within the project configuration. +// Services with unsupported hosts (e.g., extension-provided hosts that are not loaded) are skipped, +// and the resulting UnsupportedServiceHostError is returned after all other services are initialized. func (pm *projectManager) Initialize(ctx context.Context, projectConfig *ProjectConfig) error { servicesStable, err := pm.importManager.ServiceStable(ctx, projectConfig) if err != nil { @@ -106,12 +108,22 @@ func (pm *projectManager) Initialize(ctx context.Context, projectConfig *Project tracing.SetUsageAttributes(fields.ProjectServiceTargetsKey.StringSlice(serviceTargets)) + var unsupportedHostErrors []error for _, svc := range servicesStable { if err := pm.serviceManager.Initialize(ctx, svc); err != nil { - return fmt.Errorf("initializing service '%s', %w", svc.Name, err) + initErr := fmt.Errorf("initializing service '%s', %w", svc.Name, err) + if _, ok := errors.AsType[*UnsupportedServiceHostError](err); ok { + unsupportedHostErrors = append(unsupportedHostErrors, initErr) + continue + } + return initErr } } + if len(unsupportedHostErrors) > 0 { + return errors.Join(unsupportedHostErrors...) + } + return nil } @@ -155,6 +167,7 @@ func (pm *projectManager) EnsureAllTools( return err } + var unsupportedHostErrors []error for _, svc := range servicesStable { if serviceFilterFn != nil && !serviceFilterFn(svc) { continue @@ -162,6 +175,10 @@ func (pm *projectManager) EnsureAllTools( svcTools, err := pm.serviceManager.GetRequiredTools(ctx, svc) if err != nil { + if _, ok := errors.AsType[*UnsupportedServiceHostError](err); ok { + unsupportedHostErrors = append(unsupportedHostErrors, fmt.Errorf("getting service required tools: %w", err)) + continue + } return fmt.Errorf("getting service required tools: %w", err) } @@ -172,6 +189,10 @@ func (pm *projectManager) EnsureAllTools( return err } + if len(unsupportedHostErrors) > 0 { + return errors.Join(unsupportedHostErrors...) + } + return nil } From 3b5bb8f62646e813879db5d28f7c8f207fd20d36 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Sun, 31 May 2026 17:59:50 -0700 Subject: [PATCH 3/3] ci: re-trigger CI pipeline