halo-dev/halo · warning · ServerWebInputException

Illegal requires version expression.

Error message

Illegal requires version expression.

What it means

Thrown as ServerWebInputException (HTTP 400) by VersionUtils.checkVersionConstraint when parsing/evaluating a SemVer constraint expression throws any exception. It wraps failures from com.github.zafarkhaja.semver (invalid version or malformed expression) into a client-facing bad-input error. Blank or '*' constraints are allowed and never reach the parser.

Source

Thrown at application/src/main/java/run/halo/app/infra/utils/VersionUtils.java:46

    /**
     * Checks if a version satisfies the specified SemVer {@link Expression} string. If the constraint is empty or null
     * then the method returns true. Constraint examples: {@code >2.0.0} (simple), {@code ">=1.4.0 & <1.6.0"} (range).
     * See <a
     * href="https://github.com/zafarkhaja/jsemver#semver-expressions-api-ranges">semver-expressions-api-ranges</a> for
     * more info.
     *
     * @param version the version to check
     * @param constraint the SemVer Expression string
     * @return true if version satisfies the constraint or if constraint was left blank
     */
    public static boolean checkVersionConstraint(String version, String constraint) {
        try {
            return StringUtils.isBlank(constraint)
                    || "*".equals(constraint)
                    || Version.parse(version).satisfies(constraint);
        } catch (Exception e) {
            throw new ServerWebInputException("Illegal requires version expression.", null, e);
        }
    }
}

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Fix the constraint expression to valid SemVer syntax (e.g. '>=1.4.0 & <2.0.0', '~1.2', '^1.2.3').
  2. Leave the constraint blank or set it to '*' if any version is acceptable.
  3. Validate the expression with a SemVer parser locally before deploying the plugin.
  4. Confirm the version being matched is a clean x.y.z SemVer string.

Example fix

// before
requires: ">>2.0.0"

// after
requires: ">=2.0.0"
Defensive patterns

Strategy: validation

Validate before calling

if (StringUtils.isNotBlank(constraint) && !"*".equals(constraint)) {
    try {
        Version.parse(version).satisfies(constraint);
    } catch (Exception parseEx) {
        throw new IllegalArgumentException("Invalid SemVer constraint: " + constraint, parseEx);
    }
}

Type guard

static boolean isValidSemverConstraint(String c) {
    if (StringUtils.isBlank(c) || "*".equals(c)) return true;
    try { Version.valueOf("1.0.0").satisfies(c); return true; } catch (Exception e) { return false; }
}

Try / catch

try {
    boolean ok = VersionUtils.checkVersionConstraint(version, requires);
} catch (ServerWebInputException e) {
    // HTTP 400 already; surface to plugin author with the parse error
    return ServerResponse.badRequest().bodyValue("Invalid 'requires' expression: " + e.getCause().getMessage());
}

Prevention

When it happens

Trigger: A plugin 'requires' / version-constraint string that is non-blank, not '*', and either is an unparseable version or a malformed SemVer expression (e.g. '>>2.0.0', 'foo', '1.x&<2').

Common situations: A plugin manifest with a typo in requires; an unsupported operator; copy-pasting npm-style ranges into a SemVer field; version strings with build metadata in an invalid position.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/8f78d2ad0cbfdd79. Report an issue: GitHub.