theonedev/onedev · error · ValidationException

Link spec is not multiple and source issue is already linked

Error message

Link spec is not multiple and source issue is already linked to another issue via this link spec

What it means

For a unidirectional link spec (no opposite) with isMultiple()==false, validate() limits the source issue to a single link of that spec. This is thrown when the source issue already has any link of the same spec (pointing at a different target than the one requested).

Source

Thrown at server-core/src/main/java/io/onedev/server/model/IssueLink.java:111

					&& 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");
			var parsedIssueQuery = getSpec().getParsedIssueQuery(getSource().getProject());
			if (!parsedIssueQuery.matches(getSource()) || !parsedIssueQuery.matches(getTarget()))
				throw new ValidationException("Link spec not allowed to link specified issues");
		}
	}
}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Unlink the source's existing link of this spec, then create the new one.
  2. Enable 'Multiple' on the link spec if several links should be allowed.
  3. Select a source issue without an existing link of this spec.

Example fix

// before
IssueLink.createLink(link); // single spec, src already linked
// after
IssueLink old = src.getLinks().stream().filter(l -> l.getSpec().equals(spec)).findFirst().orElse(null);
if (old != null) old.remove();
IssueLink.createLink(link);
Defensive patterns

Strategy: validation

Validate before calling

boolean hasLink = source.getLinks().stream().anyMatch(l -> l.getSpec().equals(spec));
if (!spec.isMultiple() && hasLink) { /* unlink old link first or abort */ }

Try / catch

try { IssueLink.createLink(link); } catch (ValidationException e) { // replace existing single link then retry }

Prevention

When it happens

Trigger: createLink where getSpec().getOpposite() == null, getSpec().isMultiple() == false, and getSource().getLinks() contains any link with an equal spec.

Common situations: 'Depends on' style spec defined as single-link; user tries to add a second dependency; automation linking a series of issues in a loop fails on the second link; spec changed to non-multiple while links 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


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/286448983d4f7b50. Report an issue: GitHub.