theonedev/onedev · error · ValidationException
Link spec is not multiple and the source issue is already li
Error message
Link spec is not multiple and the source issue is already linked to another issue via this link spec
What it means
IssueLink.validate() enforces the constraints configured on a LinkSpec before an issue link is persisted. When the spec has an opposite (bidirectional) spec and isMultiple() is false, a source issue may hold at most one link of that spec. This ValidationException is thrown when the source issue already has a link with the same spec pointing at a different target issue.
Source
Thrown at server-core/src/main/java/io/onedev/server/model/IssueLink.java:94
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public void validate() {
if (getSource().equals(getTarget()))
throw new ValidationException("Cannot link to self");
if (getSpec().getOpposite() != null) {
if (getSource().getTargetLinks().stream()
.anyMatch(it -> it.getSpec().equals(getSpec()) && it.getTarget().equals(getTarget())))
throw new ValidationException("Source issue already linked to target issue via specified link spec");
if (!getSpec().isMultiple()
&& getSource().getTargetLinks().stream().anyMatch(it -> it.getSpec().equals(getSpec())))
throw new ValidationException(
"Link spec is not multiple and the source issue is already linked to another issue via this link spec");
if (!getSpec().getParsedIssueQuery(getSource().getProject()).matches(getTarget()))
throw new ValidationException("Link spec not allowed to link to the target issue");
if (!getSpec().getOpposite().isMultiple()
&& getTarget().getSourceLinks().stream().anyMatch(it -> it.getSpec().equals(getSpec())))
throw new ValidationException(
"Opposite side of link spec is not multiple and the target issue is already linked to another issue via this link spec");
if (!getSpec().getOpposite().getParsedIssueQuery(getSource().getProject())
.matches(getSource()))
throw new ValidationException("Opposite side of link spec not allowed to link to the source issue");
} else {
if (getSource().getLinks().stream().anyMatch(it -> it.getSpec().equals(getSpec())
&& it.getLinked(getSource()).equals(getTarget())))
throw new ValidationException("Specified issues already linked via specified link spec");
if (!getSpec().isMultiple()
&& getSource().getLinks().stream().anyMatch(it -> it.getSpec().equals(getSpec())))
throw new ValidationException(
"Link spec is not multiple and source issue is already linked to another issue via this link spec");View on GitHub (pinned to d44925c47c)
Solutions
- Check the existing links of the source issue for this spec and delete/unlink the old link before creating the new one (or link the new target and remove the old).
- Edit the link spec in Administration > Links and enable 'Multiple' if an issue should legitimately have several links of this type.
- If running an import/automation, catch ValidationException per pair and skip or pre-clean duplicates.
- Use a spec with isMultiple()==true for many-to-many relationships like 'relates to'.
Example fix
// before issueLink = new IssueLink(); issueLink.setSource(src); issueLink.setTarget(tgt); issueLink.setSpec(singleSpec); IssueLink.createLink(issueLink); // after IssueLink existing = src.getTargetLinks().stream().filter(l -> l.getSpec().equals(singleSpec)).findFirst().orElse(null); if (existing != null) existing.remove(); IssueLink.createLink(issueLink);
Defensive patterns
Strategy: validation
Validate before calling
boolean canLink = issueLink.getSpec().isMultiple() || issueLink.getSource().getTargetLinks().stream().noneMatch(l -> l.getSpec().equals(issueLink.getSpec()));
if (!canLink) { /* unlink existing link or abort */ } Try / catch
try { IssueLink.createLink(issueLink); } catch (ValidationException e) { // remove existing link of this spec and retry once, or surface to user } Prevention
- Prefer multiple=true for specs like 'relates to' or 'blocks'
- Before linking, scan the issue's links for the spec and replace rather than add
- In batch imports, deduplicate (source, spec) pairs first
When it happens
Trigger: Calling IssueLink.createLink (or the REST/UI link-issue operation) where getSpec().getOpposite() != null, getSpec().isMultiple() == false, and getSource().getTargetLinks() already contains a link with an equal spec whose target differs from the requested target.
Common situations: Admin defined a 'is blocked by / blocks' link as single-link (not multiple); user tries to add a second blocker to an issue; automation scripts or importers create several links of a single-capacity spec in a loop and hit the second iteration; project settings changed the spec to non-multiple after multiple links already existed.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- State is required
- Issue ${issueReference} is not in current project
- No permission to add specified link for specified issues
- Pull request submitter cannot be reviewer
- Job name is required
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/f98fdc58e115529a.
Report an issue: GitHub.