validate
Validate the configuration file without communicating with cloud providers. This checks syntax, type correctness, resource schema compliance, and module resolution.
Usage
carina validate [PATH]
PATH defaults to . (current directory). It must be a directory containing one or more .crn files — single-file paths are rejected.
What It Checks
- Syntax — Parses all
.crnfiles using the Carina grammar. Reports line and column for parse errors. - Resource types — Validates that resource types exist in the provider schema (e.g.,
aws.s3.Bucket). - Attribute types — Checks that attribute values match the expected types defined in the resource schema.
- Module resolution — Resolves
importstatements and validates module arguments. - Unused bindings — Warns about
letbindings that are never referenced. These can be replaced with anonymous resources. - Duplicate attributes — Warns when the same attribute key appears multiple times in a resource block. The last value wins, but this is likely unintentional.
Output
On success, Carina prints the number of validated resources and lists each resource ID:
Validating...
✓ 3 resources validated successfully.
• aws.s3.Bucket.my-bucket
• aws.ec2.Vpc.main
• aws.ec2.Subnet.public
Resources inside a for loop over an unresolved value
When a for loop iterates a value that is not known until plan/apply
time (for example a provider-computed attribute such as
cert.domain_validation_options), the loop body is enumerated as a
single deferred placeholder entry rather than expanded:
✓ 2 resources validated successfully.
• aws.acm.Certificate.cert
• aws.route53.RecordSet._domain_validation_options[?] (deferred: for _, opt in cert.domain_validation_options @ records.crn:1)
The [?] marks an address the planner will assign once the iterable
resolves, and the (deferred: <for header> @ <file>:<line>) suffix
identifies the loop (<file> is the path as passed to
carina validate; shortened to a basename above for readability). These entries are not resolvable resource
addresses — they describe a loop body, not a single resource. The
count includes one entry per deferred loop (its element count is
unknowable at validate time). The same entries appear in the
resources array of --json output.
Warnings are printed after the success message:
⚠ Unused let binding 'temp'. Consider using an anonymous resource instead.
⚠ main.crn:Duplicate attribute 'tags' at line 12 (first defined on line 8). The last value will be used.
On failure, Carina prints the error and exits with code 1.
Flags
--json
Output results as structured JSON instead of human-readable text.
carina validate --json
{
"status": "ok",
"resource_count": 3,
"resources": [
"aws.s3.Bucket.my-bucket",
"aws.ec2.Vpc.main",
"aws.ec2.Subnet.public"
]
}
When warnings are present, they are included in the output:
{
"status": "ok",
"resource_count": 2,
"resources": ["aws.s3.Bucket.my-bucket", "aws.ec2.Vpc.main"],
"warnings": [
{"type": "unused_binding", "message": "Unused let binding 'temp'"},
{"type": "duplicate_attribute", "message": "Duplicate attribute 'tags' at line 12", "file": "main.crn"}
]
}
Examples
Validate the current directory:
carina validate
Validate a specific directory:
carina validate path/to/config