pulumi/pulumi · error
removing policy group: %w
Error message
removing policy group: %w
What it means
This error is returned by Client.DeletePolicyGroup when the underlying REST DELETE call to the Pulumi Cloud policy-group endpoint fails. The %w wrapping preserves the original transport/API error (network failure, 401/403, 404, etc.) so the caller can unwrap it with errors.Is/As. Note the service rejects deletion of an organization's default policy group, which is a common 4xx cause.
Source
Thrown at pkg/backend/httpstate/client/client.go:1669
func (pc *Client) UpdatePolicyGroup(
ctx context.Context, orgName, policyGroup string, req apitype.UpdatePolicyGroupRequest,
) error {
if err := pc.restCall(
ctx, http.MethodPatch, updatePolicyGroupPath(orgName, policyGroup), nil, req, nil,
); err != nil {
return fmt.Errorf("updating policy group: %w", err)
}
return nil
}
// DeletePolicyGroup deletes a Policy Group from the given organization. The
// organization's default Policy Group cannot be deleted; the service will
// reject such requests.
func (pc *Client) DeletePolicyGroup(ctx context.Context, orgName, policyGroup string) error {
if err := pc.restCall(
ctx, http.MethodDelete, updatePolicyGroupPath(orgName, policyGroup), nil, nil, nil,
); err != nil {
return fmt.Errorf("removing policy group: %w", err)
}
return nil
}
// ListOrganizationMembers returns a single page of members for the given
// organization, wrapping the `ListOrganizationMembers` Pulumi Cloud REST
// endpoint (GET /api/orgs/{orgName}/members).
//
// mode selects between "frontend" members (data stored in the Pulumi Service's
// database) and "backend" members (data stored in the organization's identity
// backend, e.g. GitHub or GitLab). When mode is empty, the service default is
// used. continuationToken pages through results when non-nil; pass the
// ContinuationToken returned by a previous response to fetch the next page.
func (pc *Client) ListOrganizationMembers(
ctx context.Context, orgName, mode string, continuationToken *string,
) (apitype.ListOrganizationMembersResponse, error) {
queryObj := struct {
Type string `url:"type,omitempty"`View on GitHub (pinned to 793f7b2e16)
Solutions
- Verify the policy group exists and is not the organization's default policy group (the service rejects deleting the default group).
- Check PULUMI_ACCESS_TOKEN is valid and the token's user has organization Admin role.
- Unwrap the error with errors.Unwrap or %v printing to see the underlying HTTP status and message from restCall.
- Retry on transient network errors; on 401/403 fix credentials/permissions instead.
Example fix
// before
if err := client.DeletePolicyGroup(ctx, orgName, groupName); err != nil {
return fmt.Errorf("removing policy group: %w", err)
}
// after
if err := client.DeletePolicyGroup(ctx, orgName, groupName); err != nil {
var restErr *apitype.ErrorResponse
if errors.As(err, &restErr) && restErr.Code == http.StatusNotFound {
// policy group already gone; treat as success
return nil
}
return fmt.Errorf("removing policy group %q in org %q: %w", groupName, orgName, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if orgName == "" || policyGroup == "" {
return errors.New("orgName and policyGroup are required")
}
// Optionally check the group exists and is not the org default first:
groups, err := client.ListPolicyGroups(ctx, orgName, nil)
if err != nil { return err }
var found *apitype.PolicyGroup
for i := range groups.PolicyGroups {
if groups.PolicyGroups[i].Name.Name == policyGroup {
found = &groups.PolicyGroups[i]
}
}
if found == nil || found.IsOrgDefault {
return fmt.Errorf("policy group %q not found or is the org default and cannot be deleted", policyGroup)
} Type guard
func isNotFoundErr(err error) bool {
var restErr *apitype.ErrorResponse
return errors.As(err, &restErr) && restErr.Code == http.StatusNotFound
} Try / catch
err := client.DeletePolicyGroup(ctx, orgName, policyGroup)
var restErr *apitype.ErrorResponse
switch {
case err == nil:
// deleted
case errors.As(err, &restErr) && restErr.Code == http.StatusNotFound:
// already gone: treat as success
case errors.As(err, &restErr) && (restErr.Code == 401 || restErr.Code == 403):
return fmt.Errorf("check token/permissions: %w", err)
default:
return fmt.Errorf("removing policy group: %w", err) // retry transient
} Prevention
- Check the group is not the org's default policy group before deleting.
- Validate the policy group name against ListPolicyGroups before deleting.
- Keep PULUMI_ACCESS_TOKEN valid and scoped to an org Admin for destructive ops.
- Handle 404 idempotently in cleanup/CI scripts.
When it happens
Trigger: Calling DeletePolicyGroup(ctx, orgName, policyGroup) when the DELETE /api/orgs/{orgName}/policygroups/{policyGroup} request fails: network error, invalid/expired PULUMI_ACCESS_TOKEN, insufficient permissions, unknown policy group name (404), or attempting to delete the organization's default policy group.
Common situations: CI jobs with rotated or missing Pulumi Cloud tokens; typo'd policy group names; users with org Member (not Admin) role trying to delete a policy group; automation accidentally targeting the default policy group.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- listing audit logs: %w
- listing policy issues: %w
- getting policy issue: %w
- List Policy Packs failed: %w
- reading response from API: %w
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/89867be769a15c53.
Report an issue: GitHub.