§ C2 · Track A · Companions

Why is my value incomplete?

The three causes of an incomplete value, each with the error pravo prints, how to read it and the fix.

Time
12 min
Level
intermediate
Needs
A7
You will learn
Which of three causes left a value incomplete? Which line of the specification fixes it?
Done when
You can name the cause behind each incomplete value line and pravo eval -c passes.

View as Markdown

On this page

A value is concrete when it is one plain value, such as "logs", 3 or true. A value is incomplete when it still allows more than one value and nothing has chosen. An incomplete value is not wrong. It is unfinished, and more information can finish it. A conflict is different: no information can repair it.

Work in an empty directory. Every case below replaces estate.cue.

Where it shows

Write a field with two choices and no default.

estate.cue
package estate

class: "standard" | "ia"
terminalexit 1
$pravo eval
$pravo validate
$pravo eval -c
class: "standard" | "ia"
Success! 0 resources, 0 data sources, 0 ephemeral, 0 function calls, 0 outputs across 0 providers.

Error: the value is not concrete:
class: incomplete value "standard" | "ia"

The commands treat an incomplete value in three ways.

Command What it does with an incomplete value
pravo eval Prints the value as it stands and succeeds.
pravo eval -e PATH Prints the value at that path as it stands and succeeds.
pravo validate Does not fail for it.
pravo eval -c Fails and lists every incomplete value.

Anything that needs a plain value fails in the same way. Examples are a string built from the field and a condition on it. Cases 1.3 and 1.4 show both.

Find the cause

  1. Run pravo eval -c. Every reported line names a path.
  2. Print the value at that path with pravo eval -e PATH.
  3. Read what is printed.
  • A type or a bound, such as int, string or >1024: cause 1.
  • A required marker, name!: string: cause 1.
  • Several choices joined by |, and no * anywhere in the source: cause 2.
  • Several choices, and a * in two places or twice in one list: cause 3.
  • A bound where you wrote a default, such as int & <=2: cause 3.

Cause 1: nothing gave the field a value

The field is a type, a bound or a required field, and no line writes a concrete value. This is the most common cause.

1.1 A type or a bound

A type such as int and a bound such as >1024 allow many values. Neither has chosen one.

estate.cue
package estate

bucket: {
	name: "logs"
	size: int
	port: >1024
}
terminalexit 1
$pravo eval
$pravo eval -c
bucket: {
	name: "logs"
	size: int
	port: >1024
}

Error: the value is not concrete:
bucket.port: incomplete value >1024:
    ./estate.cue:6:8
bucket.size: incomplete value int:
    ./estate.cue:5:8

pravo eval prints both fields as written. pravo eval -c lists them. Each location is the line that wrote the type or the bound. Write a value that satisfies it, in any file of the package.

estate.cuevalid
package estate

bucket: {
	name: "logs"
	size: int
	port: >1024
}

bucket: {
	size: 20
	port: 8080
}
terminal
$pravo eval -c
bucket: {
	name: "logs"
	size: 20
	port: 8080
}

1.2 A required field

estate.cue
package estate

bucket: {
	name!: string
	size:  20
}
terminalexit 1
$pravo eval -c
Error: the value is not concrete:
bucket.name: field is required but not present:
    ./estate.cue:4:2

The message is field is required but not present. The location is the line that declares name!. It points at the demand, not at the place where the value belongs. Write the value in any file of the package.

estate.cuevalid
package estate

bucket: {
	name!: string
	size:  20
}

bucket: name: "logs"
terminal
$pravo eval -c
bucket: {
	name: "logs"
	size: 20
}

1.3 A value used inside a string

estate.cue
package estate

bucket: {
	name:  string
	label: "bucket-\(name)"
}
terminalexit 1
$pravo eval
$pravo eval -c
bucket: {
	name:  string
	label: "bucket-\(name)"
}

Error: the value is not concrete:
bucket.name: incomplete value string:
    ./estate.cue:4:9
bucket.label: invalid interpolation: non-concrete value string (type string):
    ./estate.cue:5:9
    ./estate.cue:4:9

pravo eval prints the string as written. pravo eval -c reports two lines. The first is name, which is incomplete. The second is label, whose string needs name. Its two locations are the string (line 5) and the field it needs (line 4). Fixing name fixes both.

estate.cuevalid
package estate

bucket: {
	name:  string
	label: "bucket-\(name)"
}

bucket: name: "logs"
terminal
$pravo eval -c
bucket: {
	name:  "logs"
	label: "bucket-logs"
}

1.4 A value used in a condition

estate.cue
package estate

bucket: {
	size: int
	if size > 10 {
		large: true
	}
}
terminalexit 1
$pravo eval
$pravo eval -c
bucket: {
	size: int
	if size > 10 {
		large: true
	}
}

Error: the value is not concrete:
bucket: non-concrete value int in operand to >:
    ./estate.cue:5:5
    ./estate.cue:4:8

An if needs a plain value to decide. pravo eval leaves the condition in place. pravo eval -c reports non-concrete value int in operand to >. The two locations are the condition (line 5) and the field it reads (line 4).

estate.cuevalid
package estate

bucket: {
	size: int
	if size > 10 {
		large: true
	}
}

bucket: size: 20
terminal
$pravo eval -c
bucket: {
	size:  20
	large: true
}

1.5 An optional field that was never set

estate.cue
package estate

bucket: {
	name:         "logs"
	description?: string
}

note: bucket.description
terminalexit 1
$pravo eval -c
Error: the value is not concrete:
note: cannot reference optional field: description:
    ./estate.cue:8:14

An optional field without a value is absent, and nothing can read an absent field. Set the field, or remove the line that reads it.

estate.cuevalid
package estate

bucket: {
	name:         "logs"
	description?: string
}

bucket: description: "Access logs"
note: bucket.description
terminal
$pravo eval -c
bucket: {
	name:        "logs"
	description: "Access logs"
}
note: "Access logs"

Cause 2: choices without a default

The field is a disjunction: a list of choices. No line selects one, and no choice is marked as the default.

2.1 A list of choices

estate.cue
package estate

class: "standard" | "ia" | "glacier"
terminalexit 1
$pravo eval -e class
$pravo eval -c
"standard" | "ia" | "glacier"

Error: the value is not concrete:
class: incomplete value "standard" | "ia" | "glacier"

The value is the whole list. There are two fixes. Write one choice as a value, for example class: "ia". Or mark one choice with * as the default (A6).

estate.cuevalid
package estate

class: *"standard" | "ia" | "glacier"
terminal
$pravo eval -c
class: "standard"

2.2 A tag that was not given

estate.cue
package estate

env: "dev" | "staging" | "prod" @tag(env)
terminalexit 1
$pravo eval -c
Error: the value is not concrete:
env: incomplete value "dev" | "staging" | "prod"

The field waits for -t env=.... Pass the tag, or mark a default.

terminal
$pravo eval -c -t env=prod
env: "prod"

Cause 3: defaults that do not agree

The field has a default somewhere, and the defaults cancel each other or are removed. The printed value looks like cause 1 or cause 2. The source shows the *.

3.1 Two defaults in two places

estate.cue
package estate

bucket: class: *"standard" | "ia" | "glacier"
prod.cueunintended
package estate

bucket: class: *"ia" | "standard" | "glacier"
terminalexit 1
$pravo eval
$pravo eval -c
bucket: {
	class: "standard" | "ia" | "glacier"
}

Error: the value is not concrete:
bucket.class: incomplete value "standard" | "ia" | "glacier"

Both files agree on the choices and disagree on the default. "standard" and "ia" cannot both be the default, so no default remains. Two * inside one list act the same way. Keep one * per field. The second file lists the choices without it.

prod.cuevalid
package estate

bucket: class: "ia" | "standard" | "glacier"
terminal
$pravo eval -c
bucket: {
	class: "standard"
}

The default "standard" stays, because only one file marked it.

3.2 A default that another constraint removes

Remove the second file from the previous case.

terminal
$rm prod.cue
estate.cue
package estate

bucket: {
	size: *3 | int
}

bucket: size: <=2
terminalexit 1
$pravo eval
$pravo eval -c
bucket: {
	size: int & <=2
}

Error: the value is not concrete:
bucket.size: incomplete value <=2 & int

The printed value, int & <=2, looks like cause 1. The default is gone. 3 is not <=2, so pravo drops it, and a type and a bound remain. Change the default to a value that satisfies every bound.

estate.cuevalid
package estate

bucket: {
	size: *2 | int
}

bucket: size: <=2
terminal
$pravo eval -c
bucket: {
	size: 2
}

In a specification with resources

Attributes that a provider fills in stay incomplete until the resource exists. pravo eval -c lists them before the first apply.

estate.cue
package estate

import loc "pravo.dev/providers/hashicorp/local"

$local: loc.#Provider

note: $local.#Resource.local_file & {
	filename: "note.txt"
	content:  "hello"
}
terminal
$pravo init
Configuration does not load yet; generating schemas for the declared providers first.
...
pravo has been initialised. Run "pravo plan" to see what it would do.
terminalexit 1
$pravo eval -c -e note.id
Error: the value is not concrete:
incomplete value string | null

The attribute id is string | null until the provider returns it. This is cause 2: two choices and no default. Apply the resource, then ask again.

terminal
$pravo apply -auto-approve | grep '^Apply complete'
$pravo eval -c -e note.id
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. [txn <id>]
"aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

After the apply, pravo fills the recorded value in. Track B explains this in B3.

Recap

  • pravo eval prints incomplete values. pravo eval -c refuses them.
  • Cause 1: a type, a bound or a required field that no line fills. Write a value.
  • Cause 2: choices with no default. Write a value, mark one default, or pass a tag.
  • Cause 3: defaults that cancel or are removed. Keep one default that satisfies every bound.

C1 explains how to read the errors above. C3 covers the errors about fields that are not allowed.