权限校验引发recursionerror主因是隐式循环调用而非递归深度不足;应通过堆栈定位反复出现的函数名,确认是否装饰器、属性访问或orm操作导致校验链式回绕,并用标记位、纯函数拆分或上下文深度限制来切断循环。

权限校验本身不该引发递归,一旦出现 RecursionError,大概率是校验逻辑中隐含了循环调用,而不是“深度不够”需要调高 recursion_limit。
先确认是不是真递归
看报错堆栈里反复出现的函数名:
- 如果全是
check_permission→check_permission→check_permission,说明校验函数在某条路径上没终止,比如:调用了自身、或通过中间层(如装饰器、钩子、信号)又绕回去了 - 如果夹杂
__getattribute__、__getattr__、_proxy_method或 ORM 的__init__/save,那很可能是访问某个属性/方法时意外触发了权限检查,而该检查又去读这个属性……形成隐式链式调用 - 如果出现
mock、pytest、__repr__,优先排查测试环境干扰,不是业务逻辑问题
常见权限校验死循环场景
这些不是“深”,而是“绕回来了”:
一款AI工具,主要用于产品经理技能,适用于 Claude Code、Codex、Cursor 和 Windsurf。涵盖 SaaS 指标诊断、PRD 评审、路线图规划、需求探索,以及面向产品经理的职业转型辅导等,适合需要提升相关任务效率的用户。
- 装饰器里调用
request.user,但该 user 对象的is_authenticated属性又触发了中间件或信号里的权限校验 - 自定义 Model 方法(如
can_edit())内部调用了self.get_permissions(),而后者又反向查了当前实例的字段,字段的 getter 又触发了权限判断 - DRF 的
has_permission中访问了request.auth,而 auth 后端的authenticate()又依赖某个需鉴权的配置服务,形成闭环 - 使用了代理对象(如 Celery task proxy、Django lazy object),其
__call__或__getattr__没做递归防护,导致无限代理跳转
怎么快速定位和修复
不用改 limit,重点是切断循环链:
- 在校验入口加简单标记:
if getattr(request, '_checking_perm', False): return False,再设request._checking_perm = True,执行完再清掉 - 用
threading.local()维护当前线程的“校验上下文深度”,超过 2 层就直接拒绝或记录告警 - 把权限判定拆成纯函数(无副作用、不访问 request/user/DB),所有状态显式传入;避免在判定过程中触发任何可能再进校验的副作用操作
- 检查是否在
__str__、__repr__、__bool__等魔术方法里调用了权限相关逻辑 —— 这些方法极易被框架隐式调用
LangGraph 场景下别混淆 recursion_limit
如果你用的是 LangGraph,recursion_limit 控制的是图执行的 SuperStep 总数,和 Python 函数递归无关。权限校验写在某个 node 里,如果它自己陷入死循环,recursion_limit=20 只会让图在第 20 步强制中断,并不会阻止校验函数内部的无限递归 —— 那部分仍会抛出 RecursionError 并崩掉整个 step。
所以 LangGraph 的 recursion_limit 是保险丝,不是解药。真正要修的,还是那个不断调用自己的校验逻辑。










