Syntax errors terraform validate makes you wait for
terraform validate is thorough, but it needs terraform init first, which means downloading providers and configuring a backend. For a quick look at a snippet from a pull request, a module README or a chat message, that is a lot of ceremony to find a missing brace. This validator parses HCL directly and reports structural errors immediately:
- Unclosed blocks:
resource "aws_s3_bucket" "site" {with no matching}, reported against the block type and the line where it opened. - Unbalanced brackets in lists, maps and function calls:
["ap-southeast-1a", "ap-southeast-1b"followed by}is “Expected ‘]’ to close ‘[’ from line 2, found ‘}’”. - Unterminated strings and templates, including a
${interpolation that never closes, and heredocs whose closing marker (EOF) is missing. - Malformed attributes:
Expected '=' after attribute, a missing value after=, or two attributes squeezed onto one line. - Malformed block headers, where the labels are not followed by
{. A classic case isbucket "pastekit-site"written without the=: HCL readsbucketas a block type with one label and then complains that no{follows. - Unterminated
/*comments. All three comment styles,#,//and/* */, are accepted and kept.
Error positions use the original file’s lines, so you can jump straight to the same spot in your editor or in the pull request diff.
How errors are presented
The first error stops the parse. Its message names the construct involved, for example “Unclosed ‘{’ of block ‘resource’ opened on line 1”, and the editor highlights that position. Hints add the likely fix, such as “Add the missing ‘}’”. While you correct it, the output pane continues to show the last version that parsed, greyed out, so the rest of your configuration remains readable.
When the file is valid, it is formatted in terraform fmt style by a built-in HCL parser and printer: one item per line, = signs aligned across consecutive attributes, strings and heredocs left exactly as written. That doubles as a check that the structure is what you think it is.
Where terraform validate is still required
This page understands HCL syntax but not Terraform semantics. It does not know provider schemas, so an invented resource type or a misspelled argument passes. It does not resolve references: var.does_not_exist or aws_s3_bucket.missing.arn are accepted as expressions. Duplicate attribute names in one block are not flagged, and type constraints in variable blocks are not evaluated. Run terraform validate (or tofu validate) and tflint in CI for those.
Terraform files often hold account IDs, internal hostnames and occasionally secrets in .tfvars. The validator runs in your browser and does not upload them.
Examples
Bucket resource missing its closing brace
Invalid: the tags map is closed but the resource block is not, so the error points to the resource’s opening brace on line 1.
resource "aws_s3_bucket" "site" {
bucket = "pastekit-site"
tags = {
Name = "site"
Environment = var.env
}
Line 1, column 33: Unclosed '{' of block 'resource' opened on line 1List closed with the wrong bracket
Invalid: the list opened with [ on line 3 meets a } first, so the validator reports which bracket it expected.
variable "zones" {
type = list(string)
default = ["ap-southeast-1a", "ap-southeast-1b"
}Line 4, column 1: Expected ']' to close '[' from line 3, found '}'Unterminated string in an output
Invalid: the closing quote is missing, and HCL strings cannot continue onto the next line.
output "site_url" {
value = "https://${aws_s3_bucket.site.bucket_regional_domain_name}
}Line 2, column 11: Unterminated string starting on line 2Valid syntax, unknown reference
Passes: the syntax is correct, and only terraform validate would notice that the subnet resource is not declared.
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
subnet_id = aws_subnet.does_not_exist.id
}resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
subnet_id = aws_subnet.does_not_exist.id
}
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Unclosed '{' of block 'resource' opened on line 1Explained | A block is missing its closing brace, often after pasting a nested block. | Add } after the last attribute of the block. |
Expected ']' to close '[' from line 3, found '}' | A list or index expression was not closed before the enclosing block ended. | Add the missing ] at the end of the list. |
Unterminated string starting on line 2 | A quoted string has no closing quote before the end of the line. | Close the quote, or use a heredoc (<<EOF) for multi-line text. |
Expected '=' after attribute 'bucket' | An attribute name is followed by a value without the equals sign. | Write it as bucket = “name”. |
Expected '{' to open block 'bucket', found the end of the line | An attribute is missing its = sign, so the name and quoted value look like a block header. | Insert = between the attribute name and its value. |
Unterminated heredoc: no closing 'EOF' line for the heredoc starting on line 4 | The closing marker line is missing or misspelled, or extra text shares its line, such as EOF followed by a comma. | Add a line containing only EOF (leading spaces are fine) right after the heredoc body. |
Frequently asked questions
Is this the same as terraform validate?
No. It only checks HCL syntax and needs no init, providers or state. terraform validate also checks resource types, arguments and references against provider schemas.
Does it work for OpenTofu, Packer, Nomad and Terragrunt files?
Yes for syntax, since they all use HCL. Tool-specific blocks and functions are not checked.
Can I validate .tfvars files?
Yes. A .tfvars file is a list of attributes, and it is checked the same way.
Does it support JSON-syntax Terraform (.tf.json)?
No. Validate those files with the JSON validator, which checks the JSON syntax they are written in.