`coerce_arg()` cannot validate a non-enum array argument: `Can't find property <ellmer::TypeBasic>@values`
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 44
- Forks
- 1
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 142
Description
Summary
A measure whose argument is an array of plain strings (or numbers, integers, booleans) is
uncallable. commons:::coerce_arg() applies the enum-only values property lookup to the array's
items type, so validation errors before the measure is ever reached. The model's correct tool
call is rejected by the harness and comes back to it as Tool calling failed with error Can't find property <ellmer::TypeBasic>@values, which it typically responds to by retrying.
Environment
- commons 0.1.0 (CRAN)
- ellmer 0.5.0
- S7 0.2.2
- R 4.5.0 (2025-04-11 ucrt), Windows 11
Reproducible example
No data, no database and no LLM required.
library(ellmer)
arr <- function(items) ellmer::tool(
function(x) x,
name = "demo",
description = "A measure with an array argument.",
arguments = list(x = ellmer::type_array(items))
)
# enum[] -- validates fine
str(commons:::validate_measure_args(arr(type_enum(values = c("a", "b"))),
list(x = c("a", "b"))))
# string[] -- e.g. `@param indicators `string[]`` on a chart measure
str(commons:::validate_measure_args(arr(type_string()),
list(x = c("a", "b"))))
Actual output (verbatim, an unrelated build-version warning trimmed):
List of 1
$ x: chr [1:2] "a" "b"
Error: Can't find property <ellmer::TypeBasic>@values
Execution halted
Expected: the second call returns List of 1 / $ x: chr [1:2] "a" "b" as well — an array of plain
strings has no value set to test membership against, so there is nothing to validate.
validate_measure_args() is the function call_measure_tool() calls, so this is the live path and
not an artefact of reaching for an internal:
call_measure (tool) -> commons:::call_measure_tool()
-> commons:::validate_measure_args()
-> commons:::coerce_arg()
Every non-enum item type fails identically:
string[] -> ERROR: Can't find property <ellmer::TypeBasic>@values
number[] -> ERROR: Can't find property <ellmer::TypeBasic>@values
integer[] -> ERROR: Can't find property <ellmer::TypeBasic>@values
boolean[] -> ERROR: Can't find property <ellmer::TypeBasic>@values
Cause
coerce_arg() (commons 0.1.0):
kind <- type_kind(type)
if (kind %in% c("enum", "array")) {
allowed <- if (kind == "enum") {
type_values(type)
} else {
type_values(S7::prop(type, "items")) # <-- unconditional
}
bad <- setdiff(as.character(value), allowed)
...
}
type_values() is S7::prop(type, "values"). That property exists on ellmer::TypeEnum and not on
ellmer::TypeBasic, so the array branch throws for any array whose items is a basic type.
The sibling helper in the same package guards the identical lookup:
array_items_label <- function(items) {
if (identical(type_kind(items), "enum")) {
paste(type_values(items), collapse = ", ")
} else {
type_kind(items)
}
}
So array_items_label() already encodes the correct rule; coerce_arg() omits it.
Consequence
Any measure with an array-of-basic-type argument is uncallable through call_measure. In our
agent, four measure arguments are string[] (e.g. an indicators: string[] overlay argument on a
chart measure), and those measures were rejected on every attempt.
Two things make it expensive to diagnose from the outside:
- The failure is attributed to the model. The tool result the model receives is a generic S7
property error with no argument name in it, so it reads as a bad call rather than as a harness
defect. Measured on one item: the model emitted exactly the intended arguments —
{"ticker":"AAPL","days":730,"indicators":["sma:200"]}— and got the error back. - The retry is unbounded and grows the conversation. The model re-searches the measure pool, gets
the same roster back, and tries again; in our case the conversation reached 17 near-identical
search results (~121 KB, 89% of the request body) and then hit the server's context ceiling as
an HTTP 400. Two different-looking transport 400s upstream of this both trace back to this one
branch.
An open grammar such as "sma:200" could not be enumerated even in principle, so the membership
test is not merely unavailable here — it is not meaningful.
Suggested fix
Guard the membership test on the item type actually having a value set, mirroring
array_items_label(), and coerce array elements by their item kind rather than always to
character. (Today the array branch ends in return(as.character(value)), so even once the
membership test is fixed a number[] argument would reach the measure as a character vector — a
second, smaller defect on the same branch.)
if (kind == "array") {
items <- S7::prop(type, "items")
if (identical(type_kind(items), "enum")) {
bad <- setdiff(as.character(value), type_values(items))
if (length(bad)) {
cli::cli_abort(...)
}
return(as.character(value))
}
return(switch(
type_kind(items),
number = as.numeric(value),
integer = as.integer(value),
boolean = as.logical(value),
as.character(value)
))
}
Happy to open a PR with tests covering string[], number[] and enum[] if that is useful.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at commons:::validate_measure_args(), follow its call to commons:::coerce_arg(), and compare the array handling with array_items_label(). Run the supplied reproducible examples and add coverage for string[], number[], integer[], boolean[], and enum[]; done means basic arrays validate without a values-property error and reach the measure with the appropriate element type.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100