xai-org/x-algorithm · error · SemanticCheckFailure
Recursive invocation of derived feature function %s is prohi
Error message
Recursive invocation of derived feature function %s is prohibited
What it means
Derived feature functions cannot invoke themselves, directly or via the current in-progress macro expansion. The compiler tracks macros being expanded (containsMacro) and rejects recursion to prevent infinite expansion.
Source
Thrown at botmaker/src/java/com/twitter/botmaker/compiler/Compiler.java:1149
private Pair<String, ASTNode> toAssignment(CompilerContext context, Tree root)
throws ParseFailure, SemanticCheckFailure {
final String variableName = getVariableName(root.getChild(0));
final Tree variableRhsNode = root.getChild(1);
final ASTNode varExprASTNode = createASTNodeTree(context, variableRhsNode);
if (varExprASTNode.getReturnType() == Type.SYMBOL) {
throw new SemanticCheckFailure("Cannot assign SYMBOL type to variable");
}
return Pair.of(variableName, varExprASTNode);
}
private ASTNode toDerivedFeatureFunction(
CompilerContext context, String funcName, int lineNo, List<ASTNode> children
) throws ParseFailure, SemanticCheckFailure {
if (context.containsMacro(funcName)) {
throw new SemanticCheckFailure(String.format(
"Recursive invocation of derived feature function %s is prohibited", funcName));
}
Optional<DerivedFeature> dfOpt = context.getDerivedFeature(funcName);
if (!dfOpt.isPresent()) {
throw new SemanticCheckFailure(String.format(
"%s is not a derived feature function", funcName));
}
DerivedFeature derivedFeature = dfOpt.get();
context.trackUsage(derivedFeature);
List<Tuple3<Tree, String, Optional<Tree>>> params = derivedFeature.getParams();
boolean isLogged = derivedFeature.isLogged();
List<ASTNode> args = children;
if (params.size() != children.size()) {
View on GitHub (pinned to 24c60942c5)
Solutions
- Rewrite the recursion as an iteration over a collection (Foldl/Foldl over a list)
- Extract the recursive step into a separate, differently named derived feature and restructure to avoid cycles
- Use a builtin like Exists/Forall over collections instead of self-recursion
Example fix
// before derived feature depth(t) = If(t.child == null, 0L, depth(t.child) + 1L) // after derived feature depth(t) = Foldl(t.children, 0L, (acc, c) -> acc + 1L)
Defensive patterns
Strategy: validation
Validate before calling
Set<String> inProgress = new HashSet<>();
if (inProgress.contains(funcName)) throw new IllegalArgumentException("Recursive derived feature: " + funcName); Try / catch
catch (SemanticCheckFailure e) { if (e.getMessage().contains("Recursive invocation")) reportRecursion(funcName); else throw e; } Prevention
- Avoid self-calls in derived feature bodies
- Express recursion as folds over collections
When it happens
Trigger: A derived feature `f` whose body calls `f(...)`, including mutual recursion routed through the same name during expansion.
Common situations: Porting recursive helper logic into a derived feature, or renaming a feature so a helper call now resolves to the feature itself.
Related errors
- %s is not a derived feature function
- Derived Feature %s has %d parameters, received %d
- Parameter #%d of %s should be %s, received %s instead
- unrecognized function name:
- class to be imported has to be either a TBase or ThriftStruc
AI-assisted analysis of xai-org/x-algorithm@24c60942c5 (2026-08-28).
Data as JSON: /api/errors/89288500cf0fa318.
Report an issue: GitHub.