{"record":{"id":"42d42d0e14035220","repo":"googleapis/mcp-toolbox","slug":"unclosed-backtick-identifier","errorCode":null,"errorMessage":"unclosed backtick identifier","messagePattern":"unclosed backtick identifier","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/bigquery/bigquerycommon/table_name_parser.go","lineNumber":421,"sourceCode":"\tvar totalConsumed int\n\n\tfor {\n\t\tremaining := s[totalConsumed:]\n\t\ttrimmed := strings.TrimLeftFunc(remaining, unicode.IsSpace)\n\t\ttotalConsumed += len(remaining) - len(trimmed)\n\t\tcurrent := s[totalConsumed:]\n\n\t\tif len(current) == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tvar part string\n\t\tvar consumed int\n\n\t\tif current[0] == '`' {\n\t\t\tend := strings.Index(current[1:], \"`\")\n\t\t\tif end == -1 {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"unclosed backtick identifier\")\n\t\t\t}\n\t\t\tpart = current[1 : end+1]\n\t\t\tconsumed = end + 2\n\t\t} else if len(current) > 0 && unicode.IsLetter(rune(current[0])) {\n\t\t\tend := strings.IndexFunc(current, func(r rune) bool {\n\t\t\t\treturn !unicode.IsLetter(r) && !unicode.IsNumber(r) && r != '_' && r != '-'\n\t\t\t})\n\t\t\tif end == -1 {\n\t\t\t\tpart = current\n\t\t\t\tconsumed = len(current)\n\t\t\t} else {\n\t\t\t\tpart = current[:end]\n\t\t\t\tconsumed = end\n\t\t\t}\n\t\t} else {\n\t\t\tbreak\n\t\t}\n","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/bigquery/bigquerycommon/table_name_parser.go#L403-L439","documentation":"parseIdentifierSequence parses dotted identifiers and supports backtick-quoted parts. When a part starts with a backtick but no closing backtick exists anywhere after it, the identifier is unterminated and cannot be extracted, so parsing fails with this error.","triggerScenarios":"A table name like `project.dataset.table` missing the closing backtick passed through parseSQL, e.g. `proj`.`ds.t or a lone stray backtick before a table name.","commonSituations":"String templates that forget to close backticks; quotes stripped by shell or YAML escaping; copy-paste from docs that mangles backticks; accidental smart-quote substitution.","solutions":["Add the missing closing backtick to the quoted identifier","Remove backticks entirely if the identifier contains no special characters (project/dataset/table names are usually unquoted-safe)","Check that templating/escaping layers (YAML, shell) are not eating the closing backtick","Use straight ASCII backticks, not typographic quotes"],"exampleFix":"// before\nSELECT * FROM `proj.ds.t;\n// after\nSELECT * FROM `proj.ds.t`;","handlingStrategy":"validation","validationCode":"func backticksClosed(sql string) bool {\n    return strings.Count(sql, \"`\")%2 == 0\n}","typeGuard":null,"tryCatchPattern":"if !backticksClosed(sql) {\n    return fmt.Errorf(\"query has an unclosed backtick identifier\")\n}\n_, err := parser.Parse(sql)\nif err != nil {\n    return err\n}","preventionTips":["Omit backticks for simple identifiers; only quote names with special characters","Beware shell/YAML backtick escaping when templating SQL","Lint SQL for balanced backticks in CI"],"tags":["bigquery","sql-parsing","identifier-quoting"],"backgroundTag":"unclosed-quoted-identifier","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}