Skip to content

findLast/findLastIndex crash inside map literal expressions #950

@aura-sohil

Description

@aura-sohil

Description

findLast() and findLastIndex() produce a runtime error when used as values inside { } map literal expressions. All other predicate-based builtin functions (find, findIndex, filter, all, any, count, none, one) work correctly in the same position.

Minimal Reproduction

package main

import (
	"fmt"
	"github.com/expr-lang/expr"
)

func main() {
	// These work fine:
	run(`findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)`)        // OK: 5
	run(`{"r": find([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`)      // OK: map[r:4]
	run(`{"r": findIndex([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`)  // OK: map[r:3]
	run(`{"r": filter([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`)     // OK: map[r:[4 5]]
	run(`{"r": all([1.0, 2.0, 3.0, 4.0, 5.0], # > 0)}`)        // OK: map[r:true]

	// These crash:
	run(`{"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`)      // ERROR
	run(`{"r": findLastIndex([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`)  // ERROR
}

func run(expression string) {
	program, err := expr.Compile(expression)
	if err != nil {
		fmt.Printf("COMPILE ERROR: %s\n  %v\n", expression, err)
		return
	}
	result, err := expr.Run(program, nil)
	if err != nil {
		fmt.Printf("RUN ERROR: %s\n  %v\n", expression, err)
		return
	}
	fmt.Printf("OK: %s\n  %v\n", expression, result)
}

Error

RUN ERROR: {"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}
  interface conversion: interface {} is bool, not string (1:1)
   | {"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}
   | ^

Versions Affected

Tested on v1.17.6 and v1.17.8 — both reproduce. The bug appears to be long-standing.

Analysis

The compilation succeeds — the error is purely at runtime (expr.Run). The predicate # > 3 returns a bool, and somewhere in the VM execution path for findLast/findLastIndex inside a MapNode, a value is type-asserted as string when it is actually bool.

find and findIndex (which iterate forward) work fine in the same position. Only the reverse-iterating variants (findLast, findLastIndex) are affected.

Workaround

Extract findLast/findLastIndex into a let binding before the map literal:

let r = findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3); {"result": r}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions