{"record":{"id":"bded5a837a25fe3b","repo":"sigoden/aichat","slug":"unexpected-call-function-name","errorCode":null,"errorMessage":"Unexpected call: {function_name} {}","messagePattern":"Unexpected call: (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/function.rs","lineNumber":243,"sourceCode":"                        vec![],\n                        Default::default(),\n                    ))\n                }\n            }\n            None => self.extract_call_config_from_config(config),\n        }\n    }\n\n    fn extract_call_config_from_config(&self, config: &GlobalConfig) -> Result<CallConfig> {\n        let function_name = self.name.clone();\n        match config.read().functions.contains(&function_name) {\n            true => Ok((\n                function_name.clone(),\n                function_name,\n                vec![],\n                Default::default(),\n            )),\n            false => bail!(\"Unexpected call: {function_name} {}\", self.arguments),\n        }\n    }\n}\n\npub fn run_llm_function(\n    cmd_name: String,\n    cmd_args: Vec<String>,\n    mut envs: HashMap<String, String>,\n) -> Result<Option<String>> {\n    let prompt = format!(\"Call {cmd_name} {}\", cmd_args.join(\" \"));\n\n    let mut bin_dirs: Vec<PathBuf> = vec![];\n    if cmd_args.len() > 1 {\n        let dir = Config::agent_functions_dir(&cmd_name).join(\"bin\");\n        if dir.exists() {\n            bin_dirs.push(dir);\n        }\n    }","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/function.rs#L225-L261","documentation":"`extract_call_config_from_config` matches the function name against configured LLM functions; when the name isn't found in config and arguments aren't in an accepted shape, it bails with 'Unexpected call'. It means the invoked tool/function is not a recognized entry in the configuration.","triggerScenarios":"A model calls a function name that has no matching entry in the `[functions]` config; called transitively by `eval` and `extract_call_config_from_agent`.","commonSituations":"Model hallucinates a tool name; user renamed/removed a function in config but the model's cached tool list still references the old name; typo in the function definition key.","solutions":["Add the missing function definition to the config under the exact name the model called","Correct the tool list exposed to the model so it matches configured functions","Check for typos between the model's call and the config key"],"exampleFix":"// before: config lacks the function\n[functions]\nlist_files = { command = \"ls\" }\n// after\n[functions]\nlist_files = { command = \"ls\" }\nread_file = { command = \"cat\" }","handlingStrategy":"validation","validationCode":"let configured: Vec<&str> = config.functions.keys().map(|s| s.as_str()).collect();\nif !configured.contains(&call_name.as_str()) {\n    eprintln!(\"function '{call_name}' is not in config: {configured:?}\");\n}","typeGuard":null,"tryCatchPattern":"match tool_call.eval(&config) {\n    Err(e) if e.to_string().starts_with(\"Unexpected call:\") => {\n        eprintln!(\"Unknown function requested by model; check [functions] config\");\n    }\n    other => other?,\n}","preventionTips":["Keep the tool list sent to the model in sync with [functions] config entries","Avoid renaming config keys after a session has started","Log all function names exposed to the model for debugging"],"tags":["config","tool-calls","unknown-function"],"backgroundTag":"resource-not-found","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}