theonedev/onedev · error · ValidationException

Specified issues already linked via specified link spec

Error message

Specified issues already linked via specified link spec

What it means

In the branch where the spec has no opposite (unidirectional link spec), validate() first rejects exact duplicates: if the source issue's links already contain a link of the same spec pointing at the same target, creating it again would be redundant, so this ValidationException is thrown.

Source

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

					.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");
			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. Skip creating the link if it already exists — check getLinks() for the spec/target pair first.
  2. Make import/automation scripts idempotent by looking up the existing IssueLink before insertion.
  3. Catch ValidationException and treat 'already linked' as success in retry logic.

Example fix

// before
IssueLink.createLink(link);
// after
boolean exists = src.getLinks().stream().anyMatch(l -> l.getSpec().equals(spec) && l.getLinked(src).equals(tgt));
if (!exists) IssueLink.createLink(link);
Defensive patterns

Strategy: validation

Validate before calling

boolean exists = source.getLinks().stream().anyMatch(l -> l.getSpec().equals(spec) && l.getLinked(source).equals(target));
if (exists) return; // already linked, nothing to do

Try / catch

try { IssueLink.createLink(link); } catch (ValidationException e) { if (e.getMessage().contains("already linked")) { /* treat as success */ } else { throw e; } }

Prevention

When it happens

Trigger: createLink called where getSpec().getOpposite() == null and getSource().getLinks() already has a link with equal spec whose getLinked(source) equals the requested target.

Common situations: User double-clicks the 'link issue' button; a REST script retries a failed request that actually succeeded (no idempotency); bulk import runs twice over the same data.

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/c6e4d267fc3541d7. Report an issue: GitHub.