eval: len() of list with errors results in non-error length?
What version of CUE are you using (cue version)?
$ cue version
cue version v0.13.0-0.dev.0.20250303124518-d73e69059114
go version go1.24.0
-buildmode exe
-compiler gc
DefaultGODEBUG gotestjsonbuildtext=1,multipathtcp=0,randseednop=0,rsa1024min=0,tlsmlkem=0,x509rsacrt=0,x509usepolicies=0
CGO_ENABLED 1
GOARCH arm64
GOOS linux
GOARM64 v8.0
vcs git
vcs.revision d73e690591145f0b19256beb5f62fc87f959b282
vcs.time 2025-03-03T12:45:18Z
vcs.modified false
cue.lang.version v0.13.0
Does this issue reproduce with the latest release?
Yes
What did you do?
# preamble
env CUE_EXPERIMENT=evalv3=1
env CUE_DEBUG=openinline=0
# export the full configuration
! exec cue export .
cmp stderr stderr.golden
# just z
! exec cue export -e z .
cmp stderr stderr.golden
-- x.cue --
package x
x: [{a!: string}, {b!: string}]
z: len(x)
-- stderr.golden --
x.0.a: field is required but not present:
./x.cue:3:6
x.1.b: field is required but not present:
./x.cue:3:20
What did you expect to see?
Passing test.
What did you see instead?
# preamble (0.000s)
# export the full configuration (0.049s)
# just z (0.045s)
> ! exec cue export -e z .
[stdout]
2
FAIL: /tmp/testscript4173821415/repro.txtar/script.txtar:10: unexpected command success
error running repro.txtar in /tmp/testscript4173821415/repro.txtar
Even though the list x is in error with respect to cue export, we can ask for its length and cue export that value.
At least through one lens, it's reasonable to assume that the result of that len() call should be an error, if its argument is in error.
This builds on the discussion in https://github.com/cue-lang/cue/issues/3711#issuecomment-2634897512.
It seems like there are at least two options:
- Require that the arguments to
len()are "not in error" with respect to the mode of evaluation and manifestation (cue exportin this case) - Add
must()so that we would instead declare:
x: [{a!: string}, {b!: string}]
z: must(isvalid(x)) & len(x)
(for some spelling of must() and isvalid())
This complaint arose originally in the "help" channel of the "CUE" Slack workspace.
It is good that cue vet catches such violations of the constraining schema, but I was hoping to be able to rely on cue export to do the same validation in addition to exporting just the projecting expression that I need out of the input data.
Thanks @seh. The context here is really useful. Because it points to a tricky aspect when it comes to defining the semantics of len():
Should it always require its arguments to be valid? Unless that answer is "yes, always" then the caller of len() needs to also think about a constraint (via must() or similar) that ensures the arguments are valid.
The reason I hesitate on the answer is that in general we want CUE to be as lazy as possible, in order that we can export/similar parts of very large configurations.
cc @rogpeppe @cuematthew for thoughts on that topic.
Should it always require its arguments to be valid? Unless that answer is "yes, always" then the caller of
len()needs to also think about a constraint (viamust()or similar) that ensures the arguments are valid.
Rather than relying on a function or operator within the language to change this preference, I'd prefer a command-line flag on cue export—and, by implication, some sort of runtime configuration set on the evaluator—that would toggle between "ensure the universe as presented is valid per unification" and "narrow the scope of interest to just this expression, and ignore the rest".
Still happens as of v0.15.0.