{"record":{"id":"f519734349e43de1","repo":"theonedev/onedev","slug":"no-permission-to-access-issue-referencestring","errorCode":null,"errorMessage":"No permission to access issue: ${referenceString}","messagePattern":"No permission to access issue: (.+?)","errorType":"http","errorClass":"UnauthorizedException","httpStatus":403,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/TodResource.java","lineNumber":466,"sourceCode":"\n        var summaries = new ArrayList<Map<String, Object>>();\n        for (var issue : issueService.query(subject, new ProjectScope(projectContext.project, true, false), parsedQuery, true, offset, count)) {\n            var summary = IssueHelper.getSummary(projectContext.currentProject, issue);\n            for (var entry: issue.getFieldInputs().entrySet()) {\n                summary.put(entry.getKey(), entry.getValue().getValues());\n            }\n            summary.put(\"link\", urlService.urlFor(issue, true));\n            summaries.add(summary);\n        }\n        return summaries;\n    }\n\n    private Issue getIssue(Project currentProject, String referenceString) {\n        var issueReference = IssueReference.of(referenceString, currentProject);\n        var issue = issueService.find(issueReference.getProject(), issueReference.getNumber());\n        if (issue != null) {\n            if (!SecurityUtils.canAccessIssue(issue))\n                throw new UnauthorizedException(\"No permission to access issue: \" + referenceString);\n            return issue;\n        } else {\n            throw new NotFoundException(\"Issue not found: \" + referenceString);\n        }\n    }\n    \n    @Path(\"/get-issue\")\n    @GET\n    public Map<String, Object> getIssueDetail(\n                @QueryParam(\"currentProject\") @NotNull String currentProjectPath, \n                @QueryParam(\"reference\") @NotNull String issueReference, \n                @QueryParam(\"forWrite\") Boolean forWrite) {\n        var subject = SecurityUtils.getSubject();\n        if (SecurityUtils.getUser(subject) == null)\n            throw new UnauthenticatedException();\n\n        var currentProject = getProject(currentProjectPath);\n        var issue = getIssue(currentProject, issueReference);                ","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/TodResource.java#L448-L484","documentation":"getIssue throws UnauthorizedException when the referenced issue exists but the current user lacks permission to see it (SecurityUtils.canAccessIssue returns false). This prevents the AI tool endpoints from leaking issue data to unauthorized users. It is distinct from not-found: the issue is there, access is denied.","triggerScenarios":"Calling issue/sourceIssue/targetIssue (e.g. via get-issue or get-issue-comments) with a reference string resolving to an existing issue in a project the authenticated user cannot access (private project, restricted role, or confidential issue).","commonSituations":"A service account token with insufficient project roles; user references an issue from another team's private project; project confidentiality settings exclude the user from the issue.","solutions":["Log in as a user with at least read access to the issue's project, or ask an admin to grant the role.","Use an access token belonging to a member of the target project.","Verify project authorization/privacy settings in OneDev admin if access should be allowed.","If the reference may point at an arbitrary project, first check the user can access that project before calling."],"exampleFix":"// before (client)\nconst issue = getIssue(currentProject, \"other-private-project#12\")\n// after\nif (!canUserAccessProject(user, \"other-private-project\")) {\n  throw new Error(\"Requesting user lacks access to other-private-project#12\")\n}\nconst issue = getIssue(currentProject, \"other-private-project#12\")","handlingStrategy":"validation","validationCode":"// pre-check the user can access the project containing the issue\nconst project = await getProject(refProjectPath);\nif (!project || !userCanAccess(project, currentUser))\n  throw new Error(`No access to project ${refProjectPath} for issue ${ref}`);","typeGuard":null,"tryCatchPattern":"try {\n  return await getIssue(ref);\n} catch (e) {\n  if (isUnauthorizedError(e)) {\n    notifyUserOfMissingPermission(ref);\n    return null; // degrade gracefully\n  }\n  throw e;\n}","preventionTips":["Check project membership/roles before referencing issues across projects.","Use service accounts with the minimum roles needed, verified against target projects.","Surface permission errors distinctly from not-found errors in the UI."],"tags":["rest","authorization","permissions"],"backgroundTag":"permission-denied","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}