conductor-oss/conductor · error · IllegalArgumentException
Composite termination must have at least one sub-condition
Error message
Composite termination must have at least one sub-condition
What it means
Thrown by buildInlineComposite() when a nested composite (an 'and'/'or' inside another composite) has a null or empty conditions list. This is the same validation as error 34 but for the inline/nested compilation path. The compiler recurses into nested composites, and each level must have at least one sub-condition.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/TerminationCompiler.java:413
+ ") { exceeded = true; reason = 'Prompt token limit exceeded'; }"
+ " if ("
+ maxCompletion
+ " > 0 && (tokenUsed.completion_tokens || 0) > "
+ maxCompletion
+ ") { exceeded = true; reason = 'Completion token limit exceeded'; }"
+ " return {should_continue: !exceeded, reason: reason};"
+ " })();";
}
/**
* Build an inline composite sub-condition. Recursively resolves nested composites. Uses a
* unique prefix to avoid variable name collisions in deeply nested composites.
*/
private static String buildInlineComposite(
TerminationConfig config, String varName, boolean isAnd, int parentIndex) {
List<TerminationConfig> conditions = config.getConditions();
if (conditions == null || conditions.isEmpty()) {
throw new IllegalArgumentException(
"Composite termination must have at least one sub-condition");
}
StringBuilder sb = new StringBuilder();
sb.append(" var ").append(varName).append(" = (function() {");
sb.append(" var results = [];");
for (int i = 0; i < conditions.size(); i++) {
String nestedVar = "s" + parentIndex + "_" + i;
TerminationConfig sub = conditions.get(i);
String nestedBody = buildNestedSubConditionBody(sub, nestedVar, parentIndex, i);
sb.append(nestedBody);
sb.append(" results.push(").append(nestedVar).append(");");
}
if (isAnd) {
sb.append(" var allTerminate = true;");
sb.append(" var reasons = [];");View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Find the nested composite (and/or) inside the conditions list that has no sub-conditions.
- Populate that nested composite's conditions with at least one TerminationConfig.
- If the nested composite is unnecessary, remove it from the parent's conditions list.
Example fix
// before: nested "or" has empty conditions
TerminationConfig.builder()
.type("and")
.conditions(List.of(
TerminationConfig.builder().type("max_message").maxMessages(10).build(),
TerminationConfig.builder().type("or").conditions(List.of()).build())) // empty nested!
.build();
// after
TerminationConfig.builder()
.type("and")
.conditions(List.of(
TerminationConfig.builder().type("max_message").maxMessages(10).build(),
TerminationConfig.builder().type("or").conditions(List.of(
TerminationConfig.builder().type("text_mention").text("STOP").build(),
TerminationConfig.builder().type("token_usage").maxTotalTokens(8000).build())).build()))
.build(); Defensive patterns
Strategy: validation
Validate before calling
void validateNestedComposites(TerminationConfig root) {
validateComposite(root);
if (root.getConditions() != null) {
for (TerminationConfig sub : root.getConditions()) {
if (Set.of("and", "or").contains(sub.getType())) {
validateNestedComposites(sub); // recurse
}
}
}
} Type guard
static boolean allNestedCompositesValid(TerminationConfig config) {
if (Set.of("and", "or").contains(config.getType())) {
if (config.getConditions() == null || config.getConditions().isEmpty()) return false;
return config.getConditions().stream().allMatch(NestedValidator::allNestedCompositesValid);
}
return true;
} Try / catch
try {
String script = TerminationCompiler.buildTerminationScript(rootConfig);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Composite termination must have at least one")) {
// find the empty nested composite and populate it or remove it
}
throw e;
} Prevention
- Recursively validate all nested composites — not just the top level.
- Remove unused empty composites from conditions lists.
- Write unit tests that exercise deeply nested termination trees.
When it happens
Trigger: A composite TerminationConfig that contains a nested composite (type='and' or 'or') whose own conditions list is null or empty. For example, an 'and' containing an 'or' that has no conditions.
Common situations: Building deeply nested termination conditions and forgetting to populate an inner composite's conditions, or a nested composite whose conditions were removed during config refactoring but the composite entry was left behind.
Related errors
- Unknown termination type in nested composite: ${type}
- Composite termination (${isAnd ? "and" : "or"}) must have at
- Unknown termination type in composite: ${type}
- Unknown termination type: ${type}
- SWARM handoff type must be on_tool_result, on_text_mention,
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/616815530fd46f0b.
Report an issue: GitHub.