Unity-Technologies/UnityCsReference · error · ExpressionNotValidException

Unknown expression: {expression}

Error message

Unknown expression: {expression}

What it means

Thrown by the Invalid evaluator (VersionRangesEvaluators.cs:62), which ExpressionTypeFactory registers as the validation function for exactly one key: ExpressionTypeKey(leftSymbol:'(', rightSymbol:')', hasLeftVersion:true) with NO separator — the shape '(V)', a single version wrapped in parentheses with no comma. Unlike errors 720-724, this expression parses AND passes the dictionary lookup successfully (GetExpression returns a VersionDefineExpression whose AppliedRule is literally 'Invalid'). The throw is DEFERRED TO EVALUATION TIME, when VersionDefineExpression.IsValid is invoked against a concrete version and dispatches to the throwing lambda. It represents a shape Unity recognizes syntactically but assigns no range semantics to.

Source

Thrown at Editor/Mono/Scripting/ScriptCompilation/VersionRangesEvaluators.cs:62

            return MinimumVersionExclusive(left, right, version)  // left < version
                && MaximumVersionExclusive(left, right, version); // && version < right;
        }

        public static bool MixedInclusiveMinimumAndExclusiveMaximumVersion(TVersion left, TVersion right, TVersion version)
        {
            return MinimumVersionInclusive(left, right, version)  // left <= version
                && MaximumVersionExclusive(left, right, version); // && version < right;
        }

        public static bool MixedExclusiveMinimumAndInclusiveMaximumVersion(TVersion left, TVersion right, TVersion version)
        {
            return MinimumVersionExclusive(left, right, version)  // left < version
                && MaximumVersionInclusive(left, right, version); // && version <= right;
        }

        public static bool Invalid(string expression)
        {
            throw new ExpressionNotValidException($"Unknown expression: {expression}");
        }
    }
}

View on GitHub (pinned to 225b0fbdb5)

Solutions

  1. For 'exactly version V' use '[V]', e.g. '[1.0]'.
  2. For 'minimum V' (x >= V) use the bare version, e.g. '1.0'.
  3. For 'strictly greater than V' (x > V) use '(V,)' WITH the comma, e.g. '(1.0,)'.
  4. Remove the parentheses entirely if you just want '>= V'.

Example fix

// before
"(1.0)"     // parses OK, AppliedRule="Invalid", throws on evaluation
// after
"[1.0]"     // x == 1.0   (or use "1.0" for x >= 1.0)
Defensive patterns

Strategy: validation

Validate before calling

// '(V)' (parens around a single version, NO comma) parses but throws on evaluation.
// Reject it up front so it never reaches the deferred Invalid evaluator.
static bool IsEvaluableShape(string e) =>
    !string.IsNullOrEmpty(e) &&
    !(e.StartsWith("(") && e.EndsWith(")") && !e.Contains(","));

// Full-shape guard (k_ValidRange) already excludes the '(V)' no-comma form.

Type guard

static bool IsNotParenOnlySingleVersion(string e) =>
    !string.IsNullOrEmpty(e) &&
    !(e.StartsWith("(") && e.EndsWith(")") && !e.Contains(","));

Try / catch

// 725 throws at EVALUATION, outside CachedVersionRangesFactory's parse-time catch.
// Either pre-check AppliedRule, or wrap the evaluation call site:
var def = factory.GetExpression(expr);          // succeeds for '(1.0)'
if (def.AppliedRule == "Invalid") {
    ReportToUser($"'{expr}' is not an evaluable version range (use '[V]' or 'V').");
    return;
}
try {
    bool active = def.IsValid(def.LeftVersion, def.RightVersion, currentVersion);
}
catch (ExpressionNotValidException ex) when (ex.Message.StartsWith("Unknown expression")) {
    ReportToUser(ex.Message);
}

Prevention

When it happens

Trigger: Using '(1.0)' as a version-define expression. GetExpression("(1.0)") returns without error because the key is registered, but when the editor later evaluates the define against the current Unity/SemVer version the IsValid lambda runs VersionRangesEvaluators<TVersion>.Invalid("(<version>)") and throws. Because CachedVersionRangesFactory only wraps GetExpression in try/catch, this evaluation-time throw escapes the cache.

Common situations: Mistakenly wrapping a single version in parentheses expecting 'exactly this version' ('(1.0)' instead of '[1.0]'); or expecting a minimum bound and adding parens ('(1.0)' instead of '1.0' or '(1.0,)'). The error surfaces inconsistently — the field validates at parse time but explodes at build/evaluate time.

Related errors


AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13). Data as JSON: /api/errors/49979ee24b48734f. Report an issue: GitHub.