datawhalechina/hello-agents · error · Exception
Theorem parameters must be uppercase letters and , only…
Error message
Theorem parameters must be uppercase letters and , only. The current theorem contains invalid characters '{str(error_paras)}'. What it means
Raised in _parse_theorem when the parameter segment returned by parse_fact() contains any character that fails str.isupper() (lowercase letters, digits, parentheses remnants, non-ASCII characters). The solver's theorem format only allows uppercase single-letter parameters separated by commas, because each character is treated as one parameter variable. Any other character makes substitution into the GPL premises impossible, so parsing is aborted.
Solutions
- Rewrite all parameters as single uppercase letters separated by commas: 'midline(ABC)' not 'midline(a,b,c1)'.
- If multi-character identifiers are required, they are unsupported by this format — map them to single uppercase letters and keep the mapping outside the solver.
- Sanitize the theorem string before calling apply/decompose: re.sub(r'[^A-Z,]', '', paras).
Example fix
# before
solver.apply('similar_triangle(tri1, tri2)') # digits + lowercase
# after
solver.apply('similar_triangle(A,B,C,D,E,F)') # single uppercase letters only Defensive patterns
Strategy: validation
Validate before calling
import re
def valid_theorem_params(theorem):
paras = theorem.split('(', 1)[1].rsplit(')', 1)[0] if '(' in theorem else ''
return re.fullmatch(r'[A-Z,]*', paras) is not None Try / catch
try:
solver.apply(theorem)
except Exception as e:
if 'uppercase letters' in str(e):
theorem = re.sub(r'[^A-Z,(]', '', theorem) # sanitize and retry once Prevention
- Generate theorem calls programmatically from uppercase point labels only.
- Add a regex gate (^[A-Z,]*$ on the param segment) in agent tool wrappers before the solver sees input.
- Tell the LLM in the tool description that parameters are single uppercase letters, comma-separated.
When it happens
Trigger: Passing a theorem whose parameters contain lowercase point names ('triangle(a,b,c)'), multi-character identifiers ('triangle(AB,CD)'), digits, or leftover formatting like underscores. Note the whole parameter string is iterated character-by-character, so commas are the only accepted separator and every other char must be A-Z.
Common situations: LLM agents generating lowercase or multi-letter point labels; users accustomed to 'point P1' style naming; parameter strings that still contain brackets after parse_fact.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ' ' has wrong number of parameters (expected ).
- Unknown relation type
- Unknown theorem name
- When the theorem name contains 'perimeter', 'area'…
- When using the 'apply' tool with theorem
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/daa671f9d0cb3aa8.
Report an issue: GitHub.
Appendix: source
Thrown at Co-creation-projects/BitSecret-GPSAgent/src/gps/symbolic_solver.py:1267
return '\n'.join(result)
def _parse_theorem(self, theorem):
try:
theorem_name, theorem_paras = parse_fact(theorem.replace(' ', ''))
except Exception as e:
e_msg = (f"Error '{repr(e)}' occurred while parsing the theorem '{theorem}'. "
f"The theorem format is incorrect.")
raise Exception(e_msg)
if theorem_name not in self.parsed_gdl["Theorems"]:
e_msg = f"Unknown theorem name: '{theorem_name}'."
raise Exception(e_msg)
error_paras = set([char for char in theorem_paras if not char.isupper()])
if len(error_paras) > 0:
e_msg = (f"Theorem parameters must be uppercase letters and , only. "
f"The current theorem contains invalid characters '{str(error_paras)}'.")
raise Exception(e_msg)
if len(theorem_paras) != 0 and len(theorem_paras) != len(self.parsed_gdl["Theorems"][theorem_name]['paras']):
e_msg = (f"'{theorem}' has wrong number of parameters "
f"(expected {len(self.parsed_gdl["Theorems"][theorem_name]['paras'])}).")
raise Exception(e_msg)
if len(theorem_paras) == 0:
theorem_paras = None
return theorem_name, theorem_paras
def apply(self, theorem):
old_fact_id = len(self.facts)
old_goal_id = len(self.goals)
old_goal_status = self.status_of_goal.copy()
theorem_name, theorem_paras = self._parse_theorem(theorem)
if theorem_paras is not None:View on GitHub (pinned to 606a07d341)