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

  1. Verify the policy group exists and is not the organization's default policy group (the service rejects deleting the default group).
  2. Check PULUMI_ACCESS_TOKEN is valid and the token's user has organization Admin role.
  3. Unwrap the error with errors.Unwrap or %v printing to see the underlying HTTP status and message from restCall.
  4. 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

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


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/89867be769a15c53. Report an issue: GitHub.