{"record":{"id":"5d781905eaa4781e","repo":"github/github-mcp-server","slug":"failed-to-get-user-permission-level-w","errorCode":null,"errorMessage":"failed to get user permission level: %w","messagePattern":"failed to get user permission level: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/lockdown/lockdown.go","lineNumber":280,"sourceCode":"\n\tif err := c.client.Query(ctx, &query, variables); err != nil {\n\t\treturn false, \"\", fmt.Errorf(\"failed to query repository metadata: %w\", err)\n\t}\n\n\tc.logDebug(ctx, fmt.Sprintf(\"queried repo access info for %s/%s: isPrivate=%t\", owner, repo, bool(query.Repository.IsPrivate)))\n\n\treturn bool(query.Repository.IsPrivate), string(query.Viewer.Login), nil\n}\n\n// checkPushAccess checks if the user has push access to the repository via the REST permission endpoint.\nfunc (c *RepoAccessCache) checkPushAccess(ctx context.Context, username, owner, repo string) (bool, error) {\n\tif c.restClient == nil {\n\t\treturn false, fmt.Errorf(\"nil REST client\")\n\t}\n\n\tpermLevel, _, err := c.restClient.Repositories.GetPermissionLevel(ctx, owner, repo, username)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to get user permission level: %w\", err)\n\t}\n\n\t// REST API maps \"maintain\" to \"write\" (and \"triage\" to \"read\")\n\t// https://docs.github.com/en/rest/collaborators/collaborators#get-repository-permissions-for-a-user\n\tpermission := permLevel.GetPermission()\n\treturn permission == \"admin\" || permission == \"write\", nil\n}\n\nfunc (c *RepoAccessCache) log(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr) {\n\tif c == nil || c.logger == nil {\n\t\treturn\n\t}\n\tif !c.logger.Enabled(ctx, level) {\n\t\treturn\n\t}\n\tc.logger.LogAttrs(ctx, level, msg, attrs...)\n}\n","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/lockdown/lockdown.go#L262-L298","documentation":"checkPushAccess calls the REST endpoint GET /repos/{owner}/{repo}/collaborators/{username}/permission to decide push access. GitHub returns 404 when the queried user is not a collaborator at all, and 403 when the caller's token lacks the collaborators permission (typical for fine-grained PATs or app tokens without it), so lockdown checks on outside contributors or least-privilege tokens surface as this wrapped error. Only admin/write count as push access.","triggerScenarios":"Lockdown evaluating content authored by a user with no collaborator status on the repo (404); a fine-grained PAT without the repository 'Collaborators: read' permission (403); a GitHub App token missing the collaborators read permission; SAML-protected org with an unauthorized token.","commonSituations":"Public repos where outside contributors' content is checked; fine-grained PATs whose permission set skipped collaborator access in the name of least privilege.","solutions":["Map a 404-wrapped failure to hasPushAccess=false instead of an error - outside users are an expected case","Give the token collaborator read access: classic PAT needs repo scope; fine-grained PAT needs repository 'Collaborators: read'","For GitHub Apps, request the collaborators read permission on the installation","Authorize the token for SAML-protected organizations when applicable"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := cache.IsSafeContent(ctx, username, owner, repo); err != nil {\n\tvar ghErr *github.ErrorResponse\n\tif errors.As(err, &ghErr) && ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotFound {\n\t\t// user is not a collaborator: that IS the answer (no push access), not a failure\n\t\tok, err = false, nil\n\t}\n}","preventionTips":["Grant the reading token 'Collaborators: read' so the permission endpoint is reachable","Treat 404 from this endpoint as a normal 'no access' answer in lockdown logic","Audit fine-grained PAT permission sets before enabling lockdown"],"tags":["go","rest","lockdown","github-api","permissions"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}