bazelbuild/bazel · error · ValueError
{tokens[1]} in {line} not surrounded by parentheses
Error message
{tokens[1]} in {line} not surrounded by parentheses What it means
Thrown by ctexplain's _ParseConfiguredTargetLine while parsing a cquery output line of the form '<label> (<config_hash>) [<fragments>]' or '<label> (null)'. It splits on whitespace (maxsplit=2) and requires the second token to start with '(' and end with ')'; otherwise it raises this ValueError showing the bad token and full line.
Source
Thrown at tools/ctexplain/bazel_api.py:149
"""Converts a cquery output line to a ConfiguredTarget.
Expected input is:
"<label> (<config hash>) [configFragment1, configFragment2, ...]"
or:
"<label> (null)"
Args:
line: The expected input.
Returns:
Corresponding ConfiguredTarget if the line matches else None.
"""
tokens = line.split(maxsplit=2)
label = tokens[0]
if tokens[1][0] != "(" or tokens[1][-1] != ")":
raise ValueError(f"{tokens[1]} in {line} not surrounded by parentheses")
config_hash = tokens[1][1:-1]
if config_hash == "null":
fragments = ()
else:
if tokens[2][0] != "[" or tokens[2][-1] != "]":
raise ValueError(f"{tokens[2]} in {line} not surrounded by [] brackets")
# The fragments list looks like '[Fragment1, Fragment2, ...]'. Split the
# whole line on ' [' to get just this list, then remove the final ']', then
# split again on ', ' to convert it to a structured tuple.
fragments = tuple(line.split(" [")[1][0:-1].split(", "))
return ConfiguredTarget(
label=label,
config=None, # Not yet available: we'll need `bazel config` to get this.
config_hash=config_hash,
transitive_fragments=fragments)
def _base_name(full_name: str) -> str:View on GitHub (pinned to e6e199d060)
Solutions
- Do not pass --output in the build flags; ctexplain depends on the default 'label (hash) [fragments]' transient format.
- Regenerate the input from 'bazel cquery' with no output-mode flags.
- If a new bazel version changed the format, update the parser in bazel_api.py to match.
Example fix
# before bazel.cquery(['deps(//a)', '--output=label_only']) # after bazel.cquery(['deps(//a)']) # keep default output format
Defensive patterns
Strategy: validation
Validate before calling
import re CQUERY_LINE = re.compile(r'^\S+ \([^)]+\)( .*)?$') lines = [l for l in cquery_stdout if CQUERY_LINE.match(l)]
Prevention
- Never pass --output flags through build_flags to ctexplain.
- Pin the bazel version whose cquery format the parser expects.
When it happens
Trigger: Feeding get_config/parse functions a line whose second whitespace token is not parenthesized — e.g. cquery run with a different --output format (label-only, starlark, jsonprogress), or lines from another tool that lack the '(hash)' segment.
Common situations: ctexplain invoked with build_flags that override --output; bazel version changing cquery's default output format; hand-edited or truncated cquery logs.
Related errors
- {tokens[2]} in {line} not surrounded by [] brackets
- Could not get config: {stderr}
- invocation failed: {stderr}
- Unfinished quote %s at %s
- Unsupported JUnit Runner API version ${JUNIT_API_VERSION_PRO
AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14).
Data as JSON: /api/errors/4bb7db5fe3df66aa.
Report an issue: GitHub.