diff --git a/zcl/zclsyntax/parser.go b/zcl/zclsyntax/parser.go
index dabdde9..1851f08 100644
--- a/zcl/zclsyntax/parser.go
+++ b/zcl/zclsyntax/parser.go
@@ -169,7 +169,7 @@ func (p *parser) finishParsingBodyAttribute(ident Token) (Node, zcl.Diagnostics)
 			if !p.recovery {
 				diags = append(diags, &zcl.Diagnostic{
 					Severity: zcl.DiagError,
-					Summary:  "Missing newline in attribute definition",
+					Summary:  "Missing newline after attribute definition",
 					Detail:   "An attribute definition must end with a newline.",
 					Subject:  &end.Range,
 					Context:  zcl.RangeBetween(ident.Range, end.Range).Ptr(),
@@ -281,6 +281,22 @@ Token:
 	diags = append(diags, bodyDiags...)
 	cBraceRange := p.PrevRange()
 
+	eol := p.Peek()
+	if eol.Type == TokenNewline || eol.Type == TokenEOF {
+		p.Read() // eat newline
+	} else {
+		if !p.recovery {
+			diags = append(diags, &zcl.Diagnostic{
+				Severity: zcl.DiagError,
+				Summary:  "Missing newline after block definition",
+				Detail:   "A block definition must end with a newline.",
+				Subject:  &eol.Range,
+				Context:  zcl.RangeBetween(ident.Range, eol.Range).Ptr(),
+			})
+		}
+		p.recoverAfterBodyItem()
+	}
+
 	return &Block{
 		Type:   blockType,
 		Labels: labels,
diff --git a/zcl/zclsyntax/parser_test.go b/zcl/zclsyntax/parser_test.go
index 5252429..17e4e65 100644
--- a/zcl/zclsyntax/parser_test.go
+++ b/zcl/zclsyntax/parser_test.go
@@ -80,6 +80,54 @@ func TestParseConfig(t *testing.T) {
 				},
 			},
 		},
+		{
+			`block {}block {}`,
+			1, // missing newline after block definition
+			&Body{
+				Attributes: Attributes{},
+				Blocks: Blocks{
+					&Block{
+						Type:   "block",
+						Labels: nil,
+						Body: &Body{
+							Attributes: Attributes{},
+							Blocks:     Blocks{},
+
+							SrcRange: zcl.Range{
+								Start: zcl.Pos{Line: 1, Column: 7, Byte: 6},
+								End:   zcl.Pos{Line: 1, Column: 9, Byte: 8},
+							},
+							EndRange: zcl.Range{
+								Start: zcl.Pos{Line: 1, Column: 9, Byte: 8},
+								End:   zcl.Pos{Line: 1, Column: 9, Byte: 8},
+							},
+						},
+
+						TypeRange: zcl.Range{
+							Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
+							End:   zcl.Pos{Line: 1, Column: 6, Byte: 5},
+						},
+						LabelRanges: nil,
+						OpenBraceRange: zcl.Range{
+							Start: zcl.Pos{Line: 1, Column: 7, Byte: 6},
+							End:   zcl.Pos{Line: 1, Column: 8, Byte: 7},
+						},
+						CloseBraceRange: zcl.Range{
+							Start: zcl.Pos{Line: 1, Column: 8, Byte: 7},
+							End:   zcl.Pos{Line: 1, Column: 9, Byte: 8},
+						},
+					},
+				},
+				SrcRange: zcl.Range{
+					Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
+					End:   zcl.Pos{Line: 1, Column: 17, Byte: 16},
+				},
+				EndRange: zcl.Range{
+					Start: zcl.Pos{Line: 1, Column: 17, Byte: 16},
+					End:   zcl.Pos{Line: 1, Column: 17, Byte: 16},
+				},
+			},
+		},
 		{
 			`block "foo" {}`,
 			0,