{"record":{"id":"ad2f9a8a5e32f831","repo":"bytebase/bytebase","slug":"failed-to-parse-project-from-policy-resource-q","errorCode":null,"errorMessage":"failed to parse project from policy resource %q","messagePattern":"failed to parse project from policy resource %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/store/policy.go","lineNumber":296,"sourceCode":"\tfor _, projectID := range projectIDs {\n\t\tresources = append(resources, common.FormatProject(projectID))\n\t}\n\tpolicies, err := s.ListPolicies(ctx, &FindPolicyMessage{\n\t\tWorkspace:    workspaceID,\n\t\tResourceType: new(storepb.Policy_PROJECT),\n\t\tResources:    resources,\n\t\tType:         new(storepb.Policy_IAM),\n\t\tShowAll:      true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpolicyMap := make(map[string]*storepb.IamPolicy, len(policies))\n\tfor _, policy := range policies {\n\t\tprojectID, err := common.GetProjectID(policy.Resource)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"failed to parse project from policy resource %q\", policy.Resource)\n\t\t}\n\t\tp := &storepb.IamPolicy{}\n\t\tif err := common.ProtojsonUnmarshaler.Unmarshal([]byte(policy.Payload), p); err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"failed to unmarshal iam policy for %v\", policy.Resource)\n\t\t}\n\t\tpolicyMap[projectID] = p\n\t}\n\treturn policyMap, nil\n}\n\nfunc (s *Store) GetWorkspaceIamPolicySnapshot(ctx context.Context, workspaceID string) (*IamPolicyMessage, error) {\n\tworkspaceResource := common.FormatWorkspace(workspaceID)\n\tkey := getIamPolicyCacheKey(workspaceID, storepb.Policy_WORKSPACE, workspaceResource)\n\tif v, ok := s.iamPolicyCache.Get(key); ok {\n\t\treturn v, nil\n\t}\n\treturn s.GetWorkspaceIamPolicy(ctx, workspaceID)\n}","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/store/policy.go#L278-L314","documentation":"ListProjectIamPolicies groups fetched policies by project. For each policy it calls common.GetProjectID(policy.Resource) to extract the project name from resource strings like \"projects/123\". errors.Wrapf(err, \"failed to parse project from policy resource %q\", policy.Resource) means a stored policy row's resource doesn't match the expected project resource format — usually a workspace-level policy (empty resource) or a malformed resource string leaked into the project-policy listing.","triggerScenarios":"GetProjectID fails on a policy returned by the underlying policy list during ListProjectIamPolicies: a workspace IAM policy row (resource not of form projects/<id>) is included by the query's filters, or a resource value with unexpected format/ID is present in the policy table.","commonSituations":"Legacy rows predating resource-format changes; a bug that stored the workspace policy with an unexpected resource; manual DB edits inserting policies with non-project resources; a filter regression in the listing query pulling in non-project policies.","solutions":["Inspect the offending policy row: check its resource value against the expected \"projects/<id>\" format.","Fix the listing query filters so only IAM policies with project resources are returned (exclude workspace/empty-resource rows).","Repair or delete malformed legacy policy rows; migrate resources that no longer parse.","Harden the loop to skip non-project policies (log and continue) if mixed resources are legitimately possible."],"exampleFix":"// before\nprojectID, err := common.GetProjectID(policy.Resource)\nif err != nil {\n\treturn nil, errors.Wrapf(err, \"failed to parse project from policy resource %q\", policy.Resource)\n}\n// after\nprojectID, err := common.GetProjectID(policy.Resource)\nif err != nil {\n\tcontinue // skip non-project policies (e.g. workspace-level rows)\n}","handlingStrategy":"fallback","validationCode":"for _, p := range policies {\n\tif _, err := common.GetProjectID(p.Resource); err != nil {\n\t\tlog.Printf(\"skipping non-project policy resource %q\", p.Resource)\n\t}\n}","typeGuard":"func isProjectPolicyResource(resource string) bool {\n\t_, err := common.GetProjectID(resource)\n\treturn err == nil\n}","tryCatchPattern":"policies, err := store.ListProjectIamPolicies(ctx)\nif err != nil && strings.Contains(err.Error(), \"failed to parse project from policy resource\") {\n\t// corrupt/legacy row: log resource for repair and degrade gracefully\n\treturn partialPoliciesWithWarning()\n}","preventionTips":["Filter policy queries to resource LIKE 'projects/%' before parsing","Skip-and-log unexpected resources instead of failing the whole listing","Migrate legacy rows whose resource format changed","Add a data-integrity check for policy resource formats"],"tags":["iam","resource-parsing","project","policy"],"backgroundTag":"invalid-argument-format","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}