--- last_reviewed: 2026-06-13 --- # 闭包内复用外层变量名导致 UnboundLocalError ## 错误现象 - 补充材料提交后,提取函数正常执行完成 - 日志停在 `travel_applications.json` 保存处,后续没有任何 Agent 处理日志 - 没有报错、没有异常堆栈,看起来像"停止" - `result.json` 里实际记录了 `{"ok": false, "error": "local variable 'agent_session' referenced before assignment"}` ## 触发条件 - 在 Flask 路由中用 `threading.Thread` 启动后台任务 - 外层作用域已有一个变量(如 `agent_session`) - 闭包 `_run()` 内对同名变量既读又写:`agent_session = run_agent_round(session_dir, agent_session, ...)` ## 原因 Python 变量作用域规则:**只要函数体内有任何对某标识符的赋值,该标识符在整个函数内都被视为局部变量**。 ```python agent_session = add_supplement(session_dir, agent_session, filenames) # 外层变量 def _run() -> None: # ... agent_session = run_agent_round( # 赋值 -> 整个 _run 内 agent_session 是局部变量 session_dir, agent_session, # 读局部变量,但此时还未赋值 -> UnboundLocalError new_files=filenames, ) ``` `run_agent_round` 调用时,`agent_session` 作为参数被求值,但此时它还是未初始化的局部变量,触发 `UnboundLocalError`。该异常被 `except BaseException` 捕获后写入 result.json,没有在日志中输出,所以表现为"静默停止"。 ## 修复方法 闭包内使用不同名称接收返回值: ```python # 修复前 agent_session = run_agent_round(session_dir, agent_session, new_files=filenames) # 修复后 new_session = run_agent_round(session_dir, agent_session, new_files=filenames) ``` 后续对返回值的引用统一改为 `new_session`。 ## 防回归要点 | 场景 | 风险 | 检查方法 | |------|------|----------| | 在闭包/嵌套函数内赋值与外层同名的变量 | UnboundLocalError | ruff F823 规则 | | `except BaseException` 吞掉异常且不打日志 | 静默失败,难以排查 | 至少记录 `log.exception` | | 用 `# noqa: F823` 压制警告而不修复 | 问题持续存在 | noqa 只应用于确认安全的场景 | **核心原则**:在闭包内需要接收外层变量的返回值时,始终使用不同的变量名。不要依赖 `nonlocal` 来修复合法性问题——换名字更简单、更安全。