{"record":{"id":"e2edbf9a28de53e6","repo":"theonedev/onedev","slug":"not-authorized-to-read-code-of-project-forkedfr","errorCode":null,"errorMessage":"Not authorized to read code of project '${forkedFrom.path}'","messagePattern":"Not authorized to read code of project '(.+?)'","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/ProjectResource.java","lineNumber":299,"sourceCode":"    @POST\n    public Long createProject(@NotNull @Valid ProjectData data) {\n\t\tvar project = new Project();\n\t\tdata.populate(project, projectService);\n\t\t\n\t\tvar subject = SecurityUtils.getSubject();\n\t\tvar user = SecurityUtils.getUser(subject);\n\n\t\tcheckProjectCreationPermission(subject, project.getParent());\n\t\n\t\tif (project.getParent() != null && project.isSelfOrAncestorOf(project.getParent())) \n\t\t\tthrow new NotAcceptableException(\"Cannot use current or descendant project as parent\");\n\t\t\n\t\tcheckProjectNameDuplication(project);\t\t\n\n\t\tif (project.getForkedFrom() != null) {\n\t\t\tvar forkedFrom = project.getForkedFrom();\n\t\t\tif (!SecurityUtils.canReadCode(subject, forkedFrom))\n\t\t\t\tthrow new UnauthorizedException(\"Not authorized to read code of project '\" + forkedFrom.getPath() + \"'\");\n\t\t\tproject.getBuildSetting().setBuildPreservations(forkedFrom.getBuildSetting().getBuildPreservations());\n\t\t\tproject.getBuildSetting().setCachePreserveDays(forkedFrom.getBuildSetting().getCachePreserveDays());\n\t\t\tproject.getBuildSetting().setJobProperties(forkedFrom.getBuildSetting().getJobProperties());\n\t\t\tproject.getBuildSetting().setDefaultFixedIssueFilters(forkedFrom.getBuildSetting().getDefaultFixedIssueFilters());\n\t\t\tproject.getBuildSetting().setListParams(forkedFrom.getBuildSetting().getListParams(false));\n\t\t\tproject.getBuildSetting().setNamedQueries(forkedFrom.getBuildSetting().getNamedQueries());\n\t\t\tproject.setPackSetting(forkedFrom.getPackSetting());\n\t\t\tproject.setPullRequestSetting(forkedFrom.getPullRequestSetting());\n\t\t\tproject.setWorkspaceSetting(forkedFrom.getWorkspaceSetting());\n\t\t\tproject.setNamedCommitQueries(forkedFrom.getNamedCommitQueries());\n\t\t\tproject.setIssueSetting(forkedFrom.getIssueSetting());\n\t\t\tproject.setNamedCodeCommentQueries(forkedFrom.getNamedCodeCommentQueries());\n\t\t\tprojectService.create(user, project);\n\t\t\tprojectService.fork(forkedFrom, project);\n\t\t} else {\n\t\t\tprojectService.create(user, project);\n\t\t}\n","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/ProjectResource.java#L281-L317","documentation":"createProject throws UnauthorizedException when the request forks from a project (forkedFrom) whose code the subject cannot read. Forking copies build settings from the source project, so read-code access on it is mandatory.","triggerScenarios":"POST /rest/projects with forkedFrom pointing at project '<path>' where SecurityUtils.canReadCode(subject, forkedFrom) is false — non-member, guest-only, or code-read permission not granted.","commonSituations":"Forking a private/team project using a personal token of a non-member; automation configured with a forkedFrom id from a different owner's project; group code-read permission removed after the automation was set up.","solutions":["Grant the account read-code permission on the fork source project (Project -> Access Control -> Code Read)","Use a token of a user who can read the source project's code","Remove forkedFrom if a plain (non-fork) project is intended","Confirm the forkedFrom path/id refers to the intended project"],"exampleFix":"// before\nPOST /rest/projects {\"name\":\"child\",\"forkedFrom\":{\"path\":\"private/lib\"}} // no code-read\n// after: grant user code-read on private/lib, or drop the field\nPOST /rest/projects {\"name\":\"child\"}","handlingStrategy":"try-catch","validationCode":"// pre-check code-read on fork source\nconst src = await api.get(`/rest/projects?path=${forkedFromPath}`);\nif (!canReadCode(currentUser, src)) throw new Error('no code-read on ' + forkedFromPath);","typeGuard":null,"tryCatchPattern":"try {\n  await api.post('/rest/projects', payload);\n} catch (e) {\n  if (e.status === 401 && /read code/.test(e.message)) {\n    // request code-read grant on the source project or drop forkedFrom\n  } else throw e;\n}","preventionTips":["Verify fork source accessibility with the same credentials before forking","Grant automation accounts code-read on all fork sources","Keep forkedFrom references updated when projects are renamed/moved","Drop forkedFrom when a plain project is intended"],"tags":["rest","authorization","fork"],"backgroundTag":"permission-denied","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}