Skip to main content

Understanding field shadowing and how to avoid it

Field shadowing is a common CUE error than can lead your plan to unexpected behavior really painful to debug.

info

Before reading this page, we recommend you to read CUE Guide.

What is field shadowing ?

It happens whenever you are using the same name as a key and value where this value is define at outer scope.
A concrete example is the best way to understand field shadowing

// We define key test with value "hello world"
test: "hello world"

// We define a simple definition with field test
#Def: {
test: string
}

// We concretise our definition and assign key test to value defined in
// outer key test
// This will produce a shadowing
shadow: #Def & {
test: test
}

// We concretise our definition and assign key test to value defined in
// outer key test but we resolve shadowing by encapsulate key with quote.
concrete: #Def & {
"test": test
}

// Result is
test: "hello world"
shadow: {
test: string // test is still a string even if we set the key because it gets shadowed
}
concrete: {
test: "hello world" // test is concrete there because we resolve shadowing with quote
}

You can see this example directly on CUE Playground here.

As you understood, for values at the same scope, sometime CUE has a hard time knowing whether you mean the other value or the key inside a definition. Putting quotes around the key when it has the same name as the value ensure CUE will not get confused, and you meet the expected behavior.

How to avoid it

Here's a concrete example of shadowing that can happens with Dagger

package main

import (
"dagger.io/dagger"
"universe.dagger.io/docker"
"universe.dagger.io/alpine"
)

dagger.#Plan & {
// Define a simple hello action
actions: hello: {
_image: alpine.#Build

MESSAGE: "hello dagger"

run: docker.#Run & {
input: _image.output
command: {
name: "/bin/sh"
args: ["-c", "echo $MESSAGE"]
}
env: MESSAGE: MESSAGE
}
}
}

If we execute this one, it will fail because MESSAGE has a conflict created from field shadowing.

dagger do hello                   
[] actions.hello.run 0.0s
[] actions.hello 0.0s
12:12PM FTL failed to execute plan: task failed: actions.hello.run._exec: actions.hello.run._exec.env.MESSAGE: non-concrete value (string|struct)

To remove this error, just wrap key MESSAGE with quotes and plan will successfully run.

run: docker.#Run & {
input: _image.output
command: {
name: "/bin/sh"
args: ["-c", "echo $MESSAGE"]
}
env: "MESSAGE": MESSAGE
}
dagger do hello
[] actions.hello.run 0.0s
[] actions.hello 0.0s