{"record":{"id":"5f8179a3b6d6640b","repo":"iflytek/astron-agent","slug":"sqlparseerror","errorCode":"SQLParseError","errorMessage":"Missing binding parameters: {missing}","messagePattern":"Missing binding parameters: (.+?)","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"error","filePath":"core/memory/database/domain/entity/general.py","lineNumber":50,"sourceCode":") -> Any:\n    \"\"\"\n    Safely parse and execute SQL with automatic parameter binding.\n\n    Args:\n        session: SQLAlchemy AsyncSession\n        sql: SQL statement with binding parameters (e.g., :username)\n        params: Parameter dictionary, e.g., {\"username\": \"alice\"}\n\n    Returns:\n        SQL execution result\n    Raises:\n        MissingSQLParamsError: If there are missing binding parameters\n    \"\"\"\n    param_names = extract_sql_params(sql)\n    provided_keys = set(params or {})\n    missing = param_names - provided_keys\n    if missing:\n        raise CustomException(\n            err_code=CodeEnum.SQLParseError.code,\n            err_msg=f\"Missing binding parameters: {missing}\",\n        )\n\n    return await session.execute(text(sql), params or {})\n\n\n@retry_on_invalid_cached_statement(max_retries=3)\nasync def exec_sql_statement(session: AsyncSession, statement: str) -> Any:\n    \"\"\"Execute raw SQL statement\n\n    Args:\n        session: SQLAlchemy AsyncSession\n        statement: SQL statement to execute\n\n    Returns:\n        SQL execution result\n    \"\"\"","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/memory/database/domain/entity/general.py#L32-L68","documentation":"parse_and_exec_sql extracts named bind parameters (:name) from the SQL and compares them with the provided params dict. If any parameter has no value, it raises CustomException with code SQLParseError before touching the database, preventing an SQLAlchemy arg error.","triggerScenarios":"Calling _exec_dml_sql / _dml_split / _get_table_column_types with SQL containing :param placeholders but params omitting one or more of those names (e.g. None or partially filled dict).","commonSituations":"Template SQL edited by hand where a placeholder was added but the caller's param map was not updated; dynamic SQL builders that skip optional fields.","solutions":["Add every missing key to the params dict before execution","Remove unused :placeholders from the SQL","Log param_names vs provided keys to find the omitted one"],"exampleFix":"// before\nawait parse_and_exec_sql(session, \"UPDATE t SET a = :a WHERE id = :id\", {\"a\": 1})\n// after\nawait parse_and_exec_sql(session, \"UPDATE t SET a = :a WHERE id = :id\", {\"a\": 1, \"id\": 7})","handlingStrategy":"validation","validationCode":"from utils import extract_sql_params\nrequired = extract_sql_params(sql)\nmissing = set(required) - set(params or {})\nif missing:\n    raise ValueError(f'provide bind params: {missing}')","typeGuard":null,"tryCatchPattern":"try:\n    await parse_and_exec_sql(session, sql, params)\nexcept CustomException as e:\n    if e.code == CodeEnum.SQLParseError.code:\n        # inspect e.message for the missing set and re-issue with params\n        ...\n    raise","preventionTips":["Build params from the same template definition that produced the SQL","Extract params with extract_sql_params before execution and assert coverage","Avoid hand-editing templated SQL placeholders"],"tags":["sql","sqlalchemy","bind-params"],"backgroundTag":"missing-required-argument","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}