CUE Error: 'conflicting values "prod" and "dev"' — Cause, Fix, and Troubleshooting Guide
Fix CUE 'conflicting values "prod" and "dev"' unification errors — two files or constraints bind the same field to incompatible concrete values.
- #iac
- #infrastructure-as-code
- #cue
- #troubleshooting
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
CUE has no assignment — it has unification. When the same field is given a value in two places, CUE unifies them: if they are compatible it keeps the most specific, but if they are two different concrete values, unification fails because there is no single value that satisfies both. That is this error:
environment: conflicting values "prod" and "dev":
./base.cue:4:18
./overlay.cue:2:18
CUE is telling you that environment is constrained to be "prod" by one file and "dev" by another, and both cannot be true at once. The two file:line references are the exact source of each value. This is not a merge that “last write wins” — CUE deliberately refuses, because silently picking one would hide a contradiction. It surfaces during cue vet, cue eval, or cue export.
Symptoms
cue vet/cue eval/cue exportfails withconflicting values "x" and "y".- Two source locations are printed, one per conflicting definition.
- It appears after adding a second file, a package import, or a
-t/tag injection. - The same field is set to a concrete value in a base and re-set in an overlay.
- Numeric or struct variants:
conflicting values 3 and 5, or field-level struct clashes.
Common Root Causes
1. The same field set to two concrete values across files
Because files in a package unify, a base and an overlay both fixing the field collide.
// base.cue
environment: "prod"
// overlay.cue (same package)
environment: "dev" // conflicts with base.cue
2. Trying to “override” a concrete value
Newcomers expect the overlay to win. In CUE, once a field is concrete you can only narrow it (to the same value), not change it.
replicas: 3
replicas: 5 // conflict, not an override
3. A constraint plus an out-of-range value
A disjunction or bound in one place and a value outside it in another.
environment: "dev" | "staging" | "prod"
environment: "qa" // conflicting values — "qa" not in the disjunction
4. Struct fields unified from two imported packages
Importing two packages that both define the same path with different concrete values.
import (
a "example.com/config/a"
b "example.com/config/b"
)
out: a.settings & b.settings // clashing concrete fields
5. Tag injection colliding with a hardcoded value
cue export -t env=dev injects into a field already pinned to "prod".
environment: string @tag(env)
environment: "prod" // -t env=dev then conflicts
How to Diagnose
Run vet to get both source locations
cue vet ./...
The two file:line:col pairs under the message are the exact conflicting definitions — open both.
Evaluate a single field to see how it resolves
cue eval -e environment ./...
This isolates the field and shows the conflict without the surrounding noise.
List which files contribute to the package
cue eval --all-errors ./... 2>&1 | head -40
--all-errors (-E) reports every conflict, not just the first, so you fix them in one pass.
Check for tag-driven conflicts
grep -rn '@tag(' .
cue export -t env=dev ./... # reproduce the injected value
Fixes
Make one side a constraint, not a concrete value
Turn the base into an open constraint (a type or disjunction) and let the overlay supply the concrete value:
// base.cue — constraint only
environment: "dev" | "staging" | "prod"
// overlay.cue — chooses one allowed value
environment: "dev"
Now unification succeeds because "dev" narrows the disjunction.
Use a default so overlays can override
The * marks a default that a concrete value replaces cleanly:
environment: *"dev" | "staging" | "prod" // default dev, override allowed
cue export yields dev unless another file narrows it to staging/prod.
Separate environments into distinct packages or files
Do not unify prod and dev in one package. Put them in sibling directories and build each on its own:
cue export ./environments/prod
cue export ./environments/dev
Fix an out-of-range value
Either widen the disjunction to include the new value or correct the value:
environment: "dev" | "staging" | "prod" | "qa" // add qa if legitimate
Reconcile imported struct clashes
Pick one source of truth for the shared field, or unify only the non-conflicting subsets rather than whole structs.
What to Watch Out For
- Model base configs as constraints (types, bounds, disjunctions) and let overlays supply concrete values — that is the CUE-idiomatic layering.
- Use
*valuedefaults for anything you expect an overlay or-ttag to override. - Keep distinct environments in distinct packages; unifying them in one package guarantees conflicts.
- Run
cue vet ./...in CI so a contradiction fails the pipeline, not the deploy. - Remember CUE is order-independent: there is no “last write wins”, so never rely on file ordering to resolve values.
Related Guides
- IaC Testing Strategies That Actually Work
- Managing Secrets in Infrastructure as Code
- GitOps for Infrastructure Explained
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.