egametang/ET · error · Exception
not found bt handler: {node.GetType().FullName}
Error message
not found bt handler: {node.GetType().FullName} What it means
BTDispatcher.Handle looked up node.GetType() in its internal btHandlers dictionary and found no registered IBTHandler for that node type. The dictionary is populated at startup by scanning all [BTHandlerAttribute]-decorated types and keying them by the result of GetNodeType(). If no handler's GetNodeType() returns this node's type, dispatch fails.
Source
Thrown at Packages/cn.etetet.behaviortree/Scripts/Model/Share/BTDispatcher.cs:64
if (ibtHandler == null)
{
throw new Exception($"bt handler not inherit IMActorHandler abstract class: {obj.GetType().FullName}");
}
Type nodeType = ibtHandler.GetNodeType();
this.btHandlers.Add(nodeType, ibtHandler);
}
public int Handle(BTNode node, BTEnv env)
{
if (node == null)
{
throw new Exception($"bt node is null");
}
if (!this.btHandlers.TryGetValue(node.GetType(), out IBTHandler ibtHandler))
{
throw new Exception($"not found bt handler: {node.GetType().FullName}");
}
int ret = ibtHandler.Handle(node, env);
#if UNITY_EDITOR
env.AddSnapshot($"Node {node.GetType().Name} {node.Id} return {ret}");
#endif
return ret;
}
}
}
View on GitHub (pinned to 5cab01f7a8)
Solutions
- Create a class that implements IBTHandler, decorate it with [BTHandlerAttribute], and have its GetNodeType() return the exact BTNode subtype being dispatched.
- Verify GetNodeType() returns the concrete node type (typeof(MyNode)), not a base type.
- Ensure the handler class is in an assembly included in the CodeTypes scan (check assembly definitions and [CodeProcess] attributes).
- Check that the handler class has [BTHandlerAttribute] — without it, BTDispatcher.Awake won't register it.
Example fix
// before — MyNode has no handler, dispatch throws
BTDispatcher.Instance.Handle(myNode, env);
// after — add a handler for MyNode
[BTHandler]
public class MyNodeHandler: IBTHandler
{
public Type GetNodeType() => typeof(MyNode);
public int Handle(BTNode node, BTEnv env)
{
var myNode = (MyNode)node;
// ... execute node logic ...
return BTResult.Success;
}
} Defensive patterns
Strategy: type-guard
Type guard
// Guard before dispatching (requires a public method on BTDispatcher):
// Add to BTDispatcher:
// public bool HasHandler(BTNode node) => btHandlers.ContainsKey(node.GetType());
//
// Then at the call site:
if (!BTDispatcher.Instance.HasHandler(node))
{
Log.Error($"No BT handler registered for node type: {node.GetType().FullName}");
return BTResult.Failure;
}
int ret = BTDispatcher.Instance.Handle(node, env); Prevention
- For every new BTNode subclass, create a matching [BTHandlerAttribute] handler whose GetNodeType() returns the exact subtype.
- Verify GetNodeType() returns the concrete node type, not a base type.
- Ensure handler classes have [BTHandlerAttribute] and are in assemblies scanned by CodeTypes.
- Add a startup test that asserts every BTNode subtype used in trees has a registered handler.
When it happens
Trigger: Dispatching a BTNode whose concrete runtime type has no corresponding handler. This happens when a new BTNode subclass is added without a matching handler, or when a handler's GetNodeType() returns a different (base) type than the node being dispatched.
Common situations: A new BTNode subtype was created but no [BTHandlerAttribute] handler class was written for it. A handler's GetNodeType() returns typeof(BaseNode) instead of typeof(ConcreteNode). The handler class is missing the [BTHandlerAttribute] decoration so it was never registered. The handler is in an assembly not scanned by CodeTypes.
Related errors
- ERR_LocationLockNotFound
- bt node is null
- bt handler not inherit IMActorHandler abstract class: {obj.G
- condition variable node must inherit BTCondition: {type.Full
- condition variable is empty: {type.FullName}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/c60405abe85b73fd.
Report an issue: GitHub.