Unity-Technologies/UnityCsReference · error · ExpressionNotValidException

'{expression}' is not a valid expression

Error message

'{expression}' is not a valid expression

What it means

Thrown by GetExpression (VersionRanges.cs:54) AFTER ParseExpression succeeds but the generated ExpressionTypeKey (left symbol, right symbol, presence of a comma separator, and presence of left/right version tokens) is not one of the range patterns registered in ExpressionTypeFactory.Create(). Every character already passed the lexical checks, so the defect is structural, not lexical: the combination of delimiters/separator/versions has no defined range meaning.

Source

Thrown at Editor/Mono/Scripting/ScriptCompilation/VersionRanges.cs:54

        public VersionDefineExpression<TVersion> GetExpression(string expression)
        {
            if (string.IsNullOrEmpty(expression))
            {
                throw new ArgumentNullException(nameof(expression));
            }

            ExpressionParsedData parsedExpressionData = ParseExpression(expression);
            if (m_ExpressionTypes.ContainsKey(parsedExpressionData.GenerateExpressionTypeKey))
            {
                ExpressionTypeValue<TVersion> expressionTypeValue = m_ExpressionTypes[parsedExpressionData.GenerateExpressionTypeKey];

                return new VersionDefineExpression<TVersion>(expressionTypeValue.IsValid, parsedExpressionData.leftVersion, parsedExpressionData.rightVersion)
                {
                    AppliedRule = expressionTypeValue.AppliedRule,
                };
            }
            throw new ExpressionNotValidException($"'{expression}' is not a valid expression");
        }

        private static bool Contains(string array, char doContain)
        {
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == doContain)
                {
                    return true;
                }
            }

            return false;
        }

        private static bool Contains(char[] array, char doContain)
        {
            for (int i = 0; i < array.Length; i++)

View on GitHub (pinned to 225b0fbdb5)

Solutions

  1. Rewrite the expression as one of the 9 supported shapes. For an inclusive closed range use '[V1,V2]'; for exclusive '(V1,V2)'; for mixed '[V1,V2)' or '(V1,V2]'.
  2. If you wrote 'V1,V2' with no brackets, change it to '[V1,V2]'.
  3. For open-ended bounds use the supported forms, NOT '[V,)': '>= V' is bare 'V'; '> V' is '(V,)'; '<= V' is '(,V]'; '< V' is '(,V)'.
  4. If one side was left empty inside brackets ('[V,]' / '[,V]'), use the proper open form above.
  5. Validate the expression with the regex/type-guard below before saving the .asmdef so it fails in tooling, not at build time.

Example fix

// before (.asmdef versionDefines expression)
"expression": "1.0,2.0"   // or "[1.0,)"  or "[,2.0]"
// after
"expression": "[1.0,2.0]" // inclusive on both ends
Defensive patterns

Strategy: validation

Validate before calling

// Matches the 9 meaningful shapes registered by ExpressionTypeFactory.
// V tokens: first char must be a digit; body chars = digit/letter . - / *
static readonly System.Text.RegularExpressions.Regex k_ValidRange =
    new System.Text.RegularExpressions.Regex(
        @"^(?:[0-9][0-9A-Za-z.\-/*]*" +                                  // V        (x >= V)
        @"|\[[0-9][0-9A-Za-z.\-/*]*\]" +                                // [V]      (x == V)
        @"|\([0-9][0-9A-Za-z.\-/*]*,\)" +                              // (V,)     (x >  V)
        @"|\(,[0-9][0-9A-Za-z.\-/*]*\]" +                             // (,V]     (x <= V)
        @"|\(,[0-9][0-9A-Za-z.\-/*]*\)" +                             // (,V)     (x <  V)
        @"|\[[0-9][0-9A-Za-z.\-/*]*,[0-9][0-9A-Za-z.\-/*]*\]" +       // [V1,V2]
        @"|\([0-9][0-9A-Za-z.\-/*]*,[0-9][0-9A-Za-z.\-/*]*\)" +       // (V1,V2)
        @"|\[[0-9][0-9A-Za-z.\-/*]*,[0-9][0-9A-Za-z.\-/*]*\)" +       // [V1,V2)
        @"|\([0-9][0-9A-Za-z.\-/*]*,[0-9][0-9A-Za-z.\-/*]*\])$",      // (V1,V2]
        System.Text.RegularExpressions.RegexOptions.Compiled);

static bool IsValidVersionRangeExpression(string expr) =>
    !string.IsNullOrEmpty(expr) && k_ValidRange.IsMatch(expr);

Type guard

static bool IsSupportedShape(string e) =>
    !string.IsNullOrEmpty(e) && k_ValidRange.IsMatch(e);

Try / catch

// CachedVersionRangesFactory already does this at parse time;
// if calling VersionRanges<TVersion>.GetExpression directly:
try { var def = ranges.GetExpression(expr); }
catch (ExpressionNotValidException ex) {
    // ex.Message == "'expr' is not a valid expression"
    ReportToUser(ex.Message);  // surface in the inspector / CI
}

Prevention

When it happens

Trigger: Calling VersionRanges<TVersion>.GetExpression(expr) (directly or via VersionRangesFactory / CachedVersionRangesFactory) with a string that parses cleanly but yields an unregistered key. Concrete inputs: '1.0,2.0' (comma range with NO surrounding brackets), '[1.0,)' (left bracket + right paren, only a left version), '[1.0,]' or '[,2.0]' (one side empty inside brackets), '(1.0]' (mismatched delimiters, no comma), '[]'. Each passes char validation but the resulting ExpressionTypeKey is absent from the dictionary.

Common situations: Authoring a Version Define in an .asmdef (Assembly Definition) inspector and typing a range without delimiters ('1.0,2.0') assuming it means a closed range; mixing bracket styles ('[1.0)' intending inclusive both ends); leaving one side empty ('[1.0,]'); or carrying over a NuGet/Maven habit like '[1.0,)' that Unity's grammar does not register. Also seen when hand-editing the versionDefines array of a .asmdef JSON by hand.

Related errors


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