{"record":{"id":"1ff13cce676c5e2f","repo":"temporalio/temporal","slug":"error-reading-contents-unmatched-end-keyword","errorCode":null,"errorMessage":"error reading contents: unmatched `END` keyword","messagePattern":"error reading contents: unmatched `END` keyword","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/query_util.go","lineNumber":147,"sourceCode":"\t\t\t\t\tif !hasWordAt(contentStr, sqlEndKeyword, j) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif ok, after := hasWordAfter(contentStr, sqlIfKeyword, j+len(sqlEndKeyword)); ok {\n\t\t\t\t\t\tif len(st) == 0 || st[len(st)-1] != sqlIfKeyword[0] {\n\t\t\t\t\t\t\treturn nil, errors.New(\"error reading contents: unmatched `END IF` keyword\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tst = st[:len(st)-1]\n\t\t\t\t\t\tj = after + len(sqlIfKeyword) - 1\n\t\t\t\t\t} else if ok, after := hasWordAfter(contentStr, sqlLoopKeyword, j+len(sqlEndKeyword)); ok {\n\t\t\t\t\t\t//nolint:revive\n\t\t\t\t\t\tif len(st) == 0 || st[len(st)-1] != sqlLoopKeyword[0] {\n\t\t\t\t\t\t\treturn nil, errors.New(\"error reading contents: unmatched `END LOOP` keyword\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tst = st[:len(st)-1]\n\t\t\t\t\t\tj = after + len(sqlLoopKeyword) - 1\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif len(st) == 0 || st[len(st)-1] != sqlBeginKeyword[0] {\n\t\t\t\t\t\t\treturn nil, errors.New(\"error reading contents: unmatched `END` keyword\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tst = st[:len(st)-1]\n\t\t\t\t\t\tj += len(sqlEndKeyword) - 1\n\t\t\t\t\t}\n\n\t\t\t\tcase sqlSingleQuote, sqlDoubleQuote:\n\t\t\t\t\tquote := contentStr[j]\n\t\t\t\t\tj++\n\t\t\t\t\tfor j < n && contentStr[j] != quote {\n\t\t\t\t\t\tj++\n\t\t\t\t\t}\n\t\t\t\t\tif j == n {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"error reading contents: unmatched quotes\")\n\t\t\t\t\t}\n\n\t\t\t\tcase sqlLineComment[0]:\n\t\t\t\t\tif j+len(sqlLineComment) <= n && contentStr[j:j+len(sqlLineComment)] == sqlLineComment {\n\t\t\t\t\t\t_, _ = stmtBuilder.Write(bytes.TrimRight(content[i:j], \" \"))","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/query_util.go#L129-L165","documentation":"A bare `END` must close an open `BEGIN` block. When the parser encounters `END` (not followed by IF/LOOP) while its block stack is empty or topped by something other than BEGIN, it returns this error, since the schema script has unbalanced block keywords.","triggerScenarios":"Calling LoadAndSplitQuery/LoadAndSplitQueryFromReaders/parseSQLStmts on a schema file with a standalone `END;` that has no enclosing `BEGIN`, or where an earlier IF/END mismatch shifted the stack so BEGIN is not on top.","commonSituations":"An earlier unmatched END IF/END LOOP popping the wrong entry (or an IF closed by bare END), desynchronizing the stack so a legitimate END appears unmatched; truncation dropping a BEGIN; dialects like T-SQL where END pairs with BEGIN differently.","solutions":["Fix any earlier unbalanced IF/LOOP blocks in the file first — a single early mismatch cascades into this error at the bare END","Ensure the standalone END has a matching BEGIN above it; add or remove accordingly","Diff against the original upstream schema file to locate the lost/extra BEGIN"],"exampleFix":"// before\nIF x THEN\n  UPDATE t SET c = 1;\nEND;\n// after\nIF x THEN\n  UPDATE t SET c = 1;\nEND IF;\n","handlingStrategy":"validation","validationCode":"beginDepth := 0\nfor _, tok := range tokenizeUpper(content) {\n    switch tok {\n    case \"BEGIN\":\n        beginDepth++\n    case \"END\":\n        beginDepth--\n        if beginDepth < 0 {\n            return errors.New(\"bare END without matching BEGIN\")\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["First fix any earlier IF/LOOP imbalance — a desynced stack surfaces as an unmatched bare END","Use END IF / END LOOP explicitly instead of bare END where the dialect allows","Keep BEGIN/END pairing per procedure in one place and avoid splicing files from different dialects"],"tags":["sql-parsing","schema","parser"],"backgroundTag":"unmatched-sql-block-keyword","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}