nektos/act · error
Compare not implemented for types: left: %+v, right: %+v
Error message
Compare not implemented for types: left: %+v, right: %+v
What it means
Thrown by the expression interpreter when a comparison operator (<, >, <=, >=, ==) is applied to operand kinds the compare switch does not handle (e.g. arrays, objects/maps, structs, slices, channels, pointers). The interpreter coerces both operands to a common reflect.Kind before comparing, and only numbers, strings, bools, and Invalid are supported. Any other kind falls into the default branch and the error reports the offending reflect Kinds.
Solutions
- Extract a scalar field before comparing: `${{ fromJSON(x).version < fromJSON(y).version }}` instead of comparing whole objects.
- Convert operands with supported functions such as toJSON/fromJSON/format/number so both sides become strings or numbers.
- Replace the raw comparison with a function that handles composites, e.g. contains(join(arr, ','), 'val') instead of arr < other.
- If you control the expression surface, pre-validate operand types in Go with reflect.ValueOf(...).Kind() before invoking the interpreter.
Example fix
# before
if: ${{ matrix.cfg < matrix.other }}
# after
if: ${{ matrix.cfg.name < matrix.other.name }} Defensive patterns
Strategy: try-catch
Validate before calling
// before invoking the interpreter, verify both comparands are scalar
func isScalar(v interface{}) bool {
switch reflect.ValueOf(v).Kind() {
case reflect.Invalid, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
return true
}
return false
} Try / catch
result, err := interpreter.Evaluate(expr)
if err != nil {
if strings.Contains(err.Error(), "Compare not implemented for types") {
// log and fall back to a string-based comparison or skip the condition
}
return err
} Prevention
- Never compare whole JSON objects/arrays in expressions; project scalar fields first with fromJSON(x).field.
- Lint workflow expressions for direct comparisons on matrix values known to be composite.
- In embedded use, reflect-check operand kinds before calling the interpreter.
When it happens
Trigger: Evaluating a workflow expression like `${{ fromJSON('{"a":1}') < fromJSON('{"a":2}') }}` or comparing a matrix value that resolved to a map/slice/array with a scalar or another object. Also triggered by comparing values whose dynamic types differ so the coercion lands on an unsupported kind (e.g. Struct vs String).
Common situations: Comparing JSON objects produced by fromJSON, comparing github event payloads (objects) instead of scalar fields, comparing two arrays from matrix generation, or comparing a null against a composite value in a way that skips the Invalid-Invalid early return.
Related errors
- TODO: ' ' not implemented
- Unable to compare incompatibles types
- failed to insert node
- Invalid glob option , available option…
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/9730c94a50182c96.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/exprparser/interpreter.go:420
return impl.compareNumber(float64(leftValue.Int()), float64(rightValue.Int()), kind)
case reflect.Float64:
if rightValue.Kind() == reflect.Int {
return impl.compareNumber(leftValue.Float(), float64(rightValue.Int()), kind)
}
return impl.compareNumber(leftValue.Float(), rightValue.Float(), kind)
case reflect.Invalid:
if rightValue.Kind() == reflect.Invalid {
return true, nil
}
// not possible situation - params are converted to the same type in code above
return nil, fmt.Errorf("Compare params of Invalid type: left: %+v, right: %+v", leftValue.Kind(), rightValue.Kind())
default:
return nil, fmt.Errorf("Compare not implemented for types: left: %+v, right: %+v", leftValue.Kind(), rightValue.Kind())
}
}
func (impl *interperterImpl) coerceToNumber(value reflect.Value) reflect.Value {
switch value.Kind() {
case reflect.Invalid:
return reflect.ValueOf(0)
case reflect.Bool:
switch value.Bool() {
case true:
return reflect.ValueOf(1)
case false:
return reflect.ValueOf(0)
}
case reflect.String:
if value.String() == "" {View on GitHub (pinned to 4f41128141)