{"record":{"id":"41814d31e266714a","repo":"temporalio/temporal","slug":"error-reading-contents-unmatched-end-loop-keywo","errorCode":null,"errorMessage":"error reading contents: unmatched `END LOOP` keyword","messagePattern":"error reading contents: unmatched `END LOOP` keyword","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/query_util.go","lineNumber":141,"sourceCode":"\t\t\t\t\tif hasWordAt(contentStr, sqlBeginKeyword, j) {\n\t\t\t\t\t\tst = append(st, sqlBeginKeyword[0])\n\t\t\t\t\t\tj += len(sqlBeginKeyword) - 1\n\t\t\t\t\t}\n\n\t\t\t\tcase sqlEndKeyword[0]:\n\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 {","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/query_util.go#L123-L159","documentation":"During statement splitting, the parser maintains a stack of BEGIN/IF/LOOP blocks. Reaching `END LOOP` when the stack is empty or the top entry is not LOOP means the schema text has an END LOOP without a matching LOOP, so LoadAndSplitQueryFromReaders aborts with this error.","triggerScenarios":"Calling LoadAndSplitQuery/LoadAndSplitQueryFromReaders on a schema file containing `END LOOP` whose corresponding `LOOP` was never opened (or was closed early/nested incorrectly).","commonSituations":"Copied CQL from another database dialect where LOOP syntax differs; a LOOP removed during editing leaving its END LOOP; nested BEGIN/LOOP pairs closed in the wrong order causing stack mismatch.","solutions":["Locate the END LOOP in the schema file and ensure a matching LOOP appears earlier at the same nesting depth","Restore the missing LOOP block or delete the stray END LOOP","Verify nesting order (BEGIN > IF/LOOP) is properly interleaved; reorder END statements so each END matches the innermost open block"],"exampleFix":"// before\nBEGIN\n  INSERT ...\nEND LOOP;\n// after\nBEGIN\n  FOR r IN list LOOP\n    INSERT ...;\n  END LOOP;\nEND;\n","handlingStrategy":"validation","validationCode":"depth := 0\nfor _, tok := range tokenizeUpper(content) {\n    switch tok {\n    case \"LOOP\":\n        depth++\n    case \"END LOOP\":\n        depth--\n        if depth < 0 {\n            return errors.New(\"END LOOP without matching LOOP\")\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure each END LOOP is preceded by LOOP at the same nesting level","Close nested blocks in reverse order of opening (LIFO)","Run schema files through the splitter in CI to catch balance errors early"],"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"}