From 71a4562bab109cca310fe5220012c6ef2a5ff812 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Mon, 25 May 2026 19:59:07 -0700 Subject: [PATCH] fix(vm): clear error when fetching field from string at runtime When a map value resolves to a string at runtime and is followed by a named-field access (e.g. v.k.missing where v.k is a string), the Fetch helper falls into the Array/Slice/String branch and calls ToInt on the field name. ToInt panics with "invalid operation: int(string)", which is confusing. Guard the branch: if the index is a string it can only be a property name, not an integer index, so panic with the canonical "cannot fetch from " message instead. Fixes #962. Signed-off-by: Sai Asish Y --- expr_test.go | 8 ++++++++ vm/runtime/runtime.go | 3 +++ 2 files changed, 11 insertions(+) diff --git a/expr_test.go b/expr_test.go index fd1ce3ab..4a34f0c4 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1586,6 +1586,14 @@ func TestExpr_fetch_from_func(t *testing.T) { assert.Contains(t, err.Error(), "cannot fetch Value from func()") } +func TestExpr_fetch_field_from_string(t *testing.T) { + // Accessing a named field on a string value (via dynamic map lookup) should + // produce a clear error instead of "invalid operation: int(string)". + _, err := expr.Eval(`let v = {"k": "hello"}; v.k.missing != ""`, nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "cannot fetch missing from string") +} + func TestExpr_map_default_values(t *testing.T) { env := map[string]any{ "foo": map[string]string{}, diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index bc6f2b4d..529fbdee 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -42,6 +42,9 @@ func Fetch(from, i any) any { switch v.Kind() { case reflect.Array, reflect.Slice, reflect.String: + if _, ok := i.(string); ok { + panic(fmt.Sprintf("cannot fetch %v from %T", i, from)) + } index := ToInt(i) l := v.Len() if index < 0 {