{"record":{"id":"9730c94a50182c96","repo":"nektos/act","slug":"compare-not-implemented-for-types-left-v-righ","errorCode":null,"errorMessage":"Compare not implemented for types: left: %+v, right: %+v","messagePattern":"Compare not implemented for types: left: %\\+v, right: %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/exprparser/interpreter.go","lineNumber":420,"sourceCode":"\t\treturn impl.compareNumber(float64(leftValue.Int()), float64(rightValue.Int()), kind)\n\n\tcase reflect.Float64:\n\t\tif rightValue.Kind() == reflect.Int {\n\t\t\treturn impl.compareNumber(leftValue.Float(), float64(rightValue.Int()), kind)\n\t\t}\n\n\t\treturn impl.compareNumber(leftValue.Float(), rightValue.Float(), kind)\n\n\tcase reflect.Invalid:\n\t\tif rightValue.Kind() == reflect.Invalid {\n\t\t\treturn true, nil\n\t\t}\n\n\t\t// not possible situation - params are converted to the same type in code above\n\t\treturn nil, fmt.Errorf(\"Compare params of Invalid type: left: %+v, right: %+v\", leftValue.Kind(), rightValue.Kind())\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"Compare not implemented for types: left: %+v, right: %+v\", leftValue.Kind(), rightValue.Kind())\n\t}\n}\n\nfunc (impl *interperterImpl) coerceToNumber(value reflect.Value) reflect.Value {\n\tswitch value.Kind() {\n\tcase reflect.Invalid:\n\t\treturn reflect.ValueOf(0)\n\n\tcase reflect.Bool:\n\t\tswitch value.Bool() {\n\t\tcase true:\n\t\t\treturn reflect.ValueOf(1)\n\t\tcase false:\n\t\t\treturn reflect.ValueOf(0)\n\t\t}\n\n\tcase reflect.String:\n\t\tif value.String() == \"\" {","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/exprparser/interpreter.go#L402-L438","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before\nif: ${{ matrix.cfg < matrix.other }}\n# after\nif: ${{ matrix.cfg.name < matrix.other.name }}","handlingStrategy":"try-catch","validationCode":"// before invoking the interpreter, verify both comparands are scalar\nfunc isScalar(v interface{}) bool {\n    switch reflect.ValueOf(v).Kind() {\n    case reflect.Invalid, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,\n         reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,\n         reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:\n        return true\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"result, err := interpreter.Evaluate(expr)\nif err != nil {\n    if strings.Contains(err.Error(), \"Compare not implemented for types\") {\n        // log and fall back to a string-based comparison or skip the condition\n    }\n    return err\n}","preventionTips":["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."],"tags":["expression","exprparser","comparison","reflect"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}