{"record":{"id":"222de7f07b1ce728","repo":"theonedev/onedev","slug":"link-spec-not-found-linkname","errorCode":null,"errorMessage":"Link spec not found: ${linkName}","messagePattern":"Link spec not found: (.+?)","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/TodResource.java","lineNumber":723,"sourceCode":"\n    @Path(\"/link-issues\")\n    @GET\n    public Map<String, Object> linkIssues(\n                @QueryParam(\"currentProject\") @NotNull String currentProjectPath, \n                @QueryParam(\"sourceReference\") @NotNull String sourceReference, \n                @QueryParam(\"linkName\") @Nullable String linkName, \n                @QueryParam(\"targetReference\") @NotNull String targetReference) {\n        if (SecurityUtils.getUser() == null)\n            throw new UnauthenticatedException();\n\n        var currentProject = getProject(currentProjectPath);\n\n        var sourceIssue = getIssue(currentProject, sourceReference);\n        var targetIssue = getIssue(currentProject, targetReference);\n\n        var linkSpec = linkSpecService.find(linkName);\n        if (linkSpec == null)\n            throw new NotFoundException(\"Link spec not found: \" + linkName);\n        if (!SecurityUtils.canEditIssueLink(sourceIssue.getProject(), linkSpec) \n                || !SecurityUtils.canEditIssueLink(targetIssue.getProject(), linkSpec)) {\n            throw new UnauthorizedException(\"No permission to add specified link for specified issues\");\n        }\n        \n        var link = new IssueLink();\n        link.setSpec(linkSpec);\n        if (linkName.equals(linkSpec.getName())) {\n            link.setSource(sourceIssue);\n            link.setTarget(targetIssue);\n        } else {\n            link.setSource(targetIssue);\n            link.setTarget(sourceIssue);\n        }\n        link.validate();\n        issueLinkService.create(link);\n\n        var linkMap = new HashMap<String, Object>();","sourceCodeStart":705,"sourceCodeEnd":741,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/TodResource.java#L705-L741","documentation":"Thrown as NotFoundException by linkIssues when linkSpecService.find(linkName) returns null, i.e., no issue link spec with the given name exists in the OneDev instance. Link specs are globally configured named relations like 'is caused by', 'depends on', etc.","triggerScenarios":"Passing a linkName query parameter that does not match any configured issue link spec (typo, renamed/deleted spec, wrong casing).","commonSituations":"Link spec renamed by an admin after the AI workflow was built; agent invents a link name; instance-specific link specs differ between environments.","solutions":["List the instance's configured issue link specs (administration) and use an exact existing name.","Correct the linkName spelling/casing.","Create the missing link spec in server settings if genuinely needed."],"exampleFix":"// before\nGET /~api/tod/link-issues?...&linkName=blocks   // spec doesn't exist\n// after\nGET /~api/tod/link-issues?...&linkName=is blocked by   // exact configured spec name","handlingStrategy":"validation","validationCode":"const specNames = await getIssueLinkSpecs(); // configured link specs\nif (!specNames.includes(linkName)) {\n  throw new Error(`linkName '${linkName}' is not a configured link spec`);\n}","typeGuard":"function isKnownLinkSpec(name, specs) {\n  return specs.some(s => s.name === name);\n}","tryCatchPattern":"try {\n  await linkIssues(...);\n} catch (e) {\n  if (e.status === 404 || /Link spec not found/.test(e.message)) {\n    // refresh the link spec list and retry with a valid name\n  } else throw e;\n}","preventionTips":["Fetch configured link specs rather than hardcoding names.","Watch for admin renames/deletions of link specs.","Keep per-environment link spec names in configuration, not code."],"tags":["not-found","link-spec","configuration","onedev"],"backgroundTag":"resource-not-found","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"}