zylon-ai/private-gpt · error · ValueError

Invalid CALL statement format

Error message

Invalid CALL statement format

What it means

Thrown by _parse_call_statement in database_query_generator.py when the regex 'CALL\s+([\w.]+)\s*\((.*?)\)' fails to match the query text. The generator only understands stored-procedure invocations of the exact form CALL schema.proc(args...), and re-parses the LLM-generated SQL to extract the procedure name, parameter values, and OUT-parameter positions. Any deviation in syntax (missing parens, empty CALL, malformed spacing that breaks the pattern) makes the parse fail and raises this ValueError.

Source

Thrown at private_gpt/components/tabular/database_query_generator.py:1278

        except Exception as e:
            return QueryResult(
                query=call_statement,
                error=ErrorQueryResult(
                    description=f"DB2 procedure execution failed: {mask_connection_secrets(str(e))}",
                    type=ErrorType.UNKNOWN,
                ),
                row_count=-1,
            )

    def _parse_call_statement(
        self, call_statement: str
    ) -> tuple[str, list[str | int], list[int]]:
        match = re.search(
            r"CALL\s+([\w.]+)\s*\((.*?)\)", call_statement, re.IGNORECASE | re.DOTALL
        )
        if not match:
            raise ValueError("Invalid CALL statement format")

        proc_name = match.group(1)
        params_str = match.group(2)

        param_values: list[str | int] = []
        out_indices: list[int] = []

        for i, param in enumerate(params_str.split(",")):
            param = param.strip()
            if param == "?":
                param_values.append(0)  # Default value for OUT parameters
                out_indices.append(i)
            else:
                param_values.append(param.strip("'\""))

        return proc_name, param_values, out_indices

    def _create_db2_connection(self) -> Any:

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Log the exact call_statement that failed to parse and inspect it for syntax the regex cannot match (missing parens, quoted identifiers, BEGIN/COMMIT wrappers).
  2. Tighten the prompt/few-shot at line 773 so the model emits exactly 'CALL schema.proc_name(arg1, ?, ?, ?);' with nothing else.
  3. Normalize the statement before parsing: strip trailing semicolons, leading BEGIN/COMMIT, and collapse whitespace.
  4. Extend the regex to allow quoted identifiers, e.g. r"CALL\s+(?:[\w.]+|\"[^\"]+\")\s*\((.*?)\)".
  5. Wrap the parse in a retry that asks the LLM to regenerate the CALL statement when a QuerySyntaxError/ValueError is raised.

Example fix

# before
match = re.search(
    r"CALL\s+([\w.]+)\s*\((.*?)\)", call_statement, re.IGNORECASE | re.DOTALL
)
if not match:
    raise ValueError("Invalid CALL statement format")

# after
normalized = re.sub(r"^(BEGIN|COMMIT)\s*;?\s*", "", call_statement.strip(), flags=re.IGNORECASE).strip().rstrip(";").strip()
match = re.search(
    r"CALL\s+([\w.]+)\s*\((.*?)\)", normalized, re.IGNORECASE | re.DOTALL
)
if not match:
    logger.error("Unparseable CALL statement: %r", call_statement)
    raise ValueError("Invalid CALL statement format")
Defensive patterns

Strategy: validation

Validate before calling

import re

CALL_RE = re.compile(r"CALL\s+([\w.]+)\s*\((.*?)\)", re.IGNORECASE | re.DOTALL)

def is_parseable_call(stmt: str) -> bool:
    stmt = stmt.strip().rstrip(';').strip()
    return bool(CALL_RE.search(stmt))

# before the generator call:
# assert is_parseable_call(llm_query), f"bad CALL statement: {llm_query!r}"

Try / catch

try:
    row_count = generator.get_row_count(result)
except ValueError as e:
    if "Invalid CALL statement format" in str(e):
        # regenerate the SQL with corrective feedback
        result = regenerate_sql(prompt_with_error(str(e)))
    else:
        raise

Prevention

When it happens

Trigger: An LLM-generated query string passed to the row-count/result path (line 1239) does not literally start with or contain 'CALL proc(...)'. Examples: the model emits 'CALL proc' with no parentheses, wraps the call in BEGIN/COMMIT despite the prompt instruction, returns prose or an empty string, or uses a quoted procedure name like CALL "my proc"(...) whose spaces break the [\w.]+ capture.

Common situations: Prompt-regression after changing the few-shot examples at line 773; models that add semicolons inside the parens or emit multiple statements; older stored procedures with uppercase/lowercase mix (handled) vs. quoted identifiers (not handled); an empty params string 'CALL p()' actually matches (group 2 is empty) but 'CALL p' does not.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/e4d973f7fef8be54. Report an issue: GitHub.