{"record":{"id":"89867be769a15c53","repo":"pulumi/pulumi","slug":"removing-policy-group-w","errorCode":null,"errorMessage":"removing policy group: %w","messagePattern":"removing policy group: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/backend/httpstate/client/client.go","lineNumber":1669,"sourceCode":"func (pc *Client) UpdatePolicyGroup(\n\tctx context.Context, orgName, policyGroup string, req apitype.UpdatePolicyGroupRequest,\n) error {\n\tif err := pc.restCall(\n\t\tctx, http.MethodPatch, updatePolicyGroupPath(orgName, policyGroup), nil, req, nil,\n\t); err != nil {\n\t\treturn fmt.Errorf(\"updating policy group: %w\", err)\n\t}\n\treturn nil\n}\n\n// DeletePolicyGroup deletes a Policy Group from the given organization. The\n// organization's default Policy Group cannot be deleted; the service will\n// reject such requests.\nfunc (pc *Client) DeletePolicyGroup(ctx context.Context, orgName, policyGroup string) error {\n\tif err := pc.restCall(\n\t\tctx, http.MethodDelete, updatePolicyGroupPath(orgName, policyGroup), nil, nil, nil,\n\t); err != nil {\n\t\treturn fmt.Errorf(\"removing policy group: %w\", err)\n\t}\n\treturn nil\n}\n\n// ListOrganizationMembers returns a single page of members for the given\n// organization, wrapping the `ListOrganizationMembers` Pulumi Cloud REST\n// endpoint (GET /api/orgs/{orgName}/members).\n//\n// mode selects between \"frontend\" members (data stored in the Pulumi Service's\n// database) and \"backend\" members (data stored in the organization's identity\n// backend, e.g. GitHub or GitLab). When mode is empty, the service default is\n// used. continuationToken pages through results when non-nil; pass the\n// ContinuationToken returned by a previous response to fetch the next page.\nfunc (pc *Client) ListOrganizationMembers(\n\tctx context.Context, orgName, mode string, continuationToken *string,\n) (apitype.ListOrganizationMembersResponse, error) {\n\tqueryObj := struct {\n\t\tType              string  `url:\"type,omitempty\"`","sourceCodeStart":1651,"sourceCodeEnd":1687,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/backend/httpstate/client/client.go#L1651-L1687","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif err := client.DeletePolicyGroup(ctx, orgName, groupName); err != nil {\n    return fmt.Errorf(\"removing policy group: %w\", err)\n}\n// after\nif err := client.DeletePolicyGroup(ctx, orgName, groupName); err != nil {\n    var restErr *apitype.ErrorResponse\n    if errors.As(err, &restErr) && restErr.Code == http.StatusNotFound {\n        // policy group already gone; treat as success\n        return nil\n    }\n    return fmt.Errorf(\"removing policy group %q in org %q: %w\", groupName, orgName, err)\n}","handlingStrategy":"try-catch","validationCode":"if orgName == \"\" || policyGroup == \"\" {\n    return errors.New(\"orgName and policyGroup are required\")\n}\n// Optionally check the group exists and is not the org default first:\ngroups, err := client.ListPolicyGroups(ctx, orgName, nil)\nif err != nil { return err }\nvar found *apitype.PolicyGroup\nfor i := range groups.PolicyGroups {\n    if groups.PolicyGroups[i].Name.Name == policyGroup {\n        found = &groups.PolicyGroups[i]\n    }\n}\nif found == nil || found.IsOrgDefault {\n    return fmt.Errorf(\"policy group %q not found or is the org default and cannot be deleted\", policyGroup)\n}","typeGuard":"func isNotFoundErr(err error) bool {\n    var restErr *apitype.ErrorResponse\n    return errors.As(err, &restErr) && restErr.Code == http.StatusNotFound\n}","tryCatchPattern":"err := client.DeletePolicyGroup(ctx, orgName, policyGroup)\nvar restErr *apitype.ErrorResponse\nswitch {\ncase err == nil:\n    // deleted\ncase errors.As(err, &restErr) && restErr.Code == http.StatusNotFound:\n    // already gone: treat as success\ncase errors.As(err, &restErr) && (restErr.Code == 401 || restErr.Code == 403):\n    return fmt.Errorf(\"check token/permissions: %w\", err)\ndefault:\n    return fmt.Errorf(\"removing policy group: %w\", err) // retry transient\n}","preventionTips":["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."],"tags":["http","pulumi-cloud","policy-groups","api-client"],"backgroundTag":"http-request-failed","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}