conductor-oss/conductor · error · IllegalArgumentException
Composite termination (${isAnd ? "and" : "or"}) must have at
Error message
Composite termination (${isAnd ? "and" : "or"}) must have at least one sub-condition What it means
Thrown by buildCompositeScript() when an 'and' or 'or' composite termination config has a null or empty 'conditions' list. A composite termination must have at least one sub-condition to evaluate — without any, the AND/OR semantics are undefined (AND of nothing is vacuously true, OR of nothing is false, both meaningless for termination).
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/TerminationCompiler.java:259
+ " return {should_continue: !exceeded, reason: reason};");
}
/**
* Build a composite (AND/OR) script by inlining each sub-condition's check.
*
* <p>For AND: all sub-conditions must signal termination (should_continue == false) for the
* composite to terminate.
*
* <p>For OR: any sub-condition signaling termination causes the composite to terminate.
*
* @param config the composite termination config
* @param isAnd true for AND semantics, false for OR
* @return a JavaScript IIFE string
*/
private static String buildCompositeScript(TerminationConfig config, boolean isAnd) {
List<TerminationConfig> conditions = config.getConditions();
if (conditions == null || conditions.isEmpty()) {
throw new IllegalArgumentException(
"Composite termination ("
+ (isAnd ? "and" : "or")
+ ") must have at least one sub-condition");
}
StringBuilder body = new StringBuilder();
body.append(" var results = [];");
for (int i = 0; i < conditions.size(); i++) {
TerminationConfig sub = conditions.get(i);
String subBody = buildSubConditionBody(sub, i);
body.append(subBody);
body.append(" results.push(r").append(i).append(");");
}
if (isAnd) {
// AND: all must signal termination (should_continue == false) for composite to
// terminateView on GitHub (pinned to cf7c3e4a8a)
Solutions
- Populate the conditions list with at least one TerminationConfig sub-condition.
- Each sub-condition must itself have a valid type (text_mention, stop_message, max_message, token_usage, or nested and/or).
- If you only need a single condition, use that condition directly instead of wrapping it in a composite.
Example fix
// before
TerminationConfig.builder()
.type("and")
.conditions(List.of()) // empty!
.build();
// after
TerminationConfig.builder()
.type("and")
.conditions(List.of(
TerminationConfig.builder().type("max_message").maxMessages(20).build(),
TerminationConfig.builder().type("text_mention").text("DONE").build()))
.build(); Defensive patterns
Strategy: validation
Validate before calling
void validateComposite(TerminationConfig config) {
if ("and".equals(config.getType()) || "or".equals(config.getType())) {
if (config.getConditions() == null || config.getConditions().isEmpty()) {
throw new IllegalArgumentException(
"Composite termination (" + config.getType() + ") needs >= 1 sub-condition");
}
}
} Type guard
static boolean isValidComposite(TerminationConfig config) {
if (!Set.of("and", "or").contains(config.getType())) return true;
return config.getConditions() != null && !config.getConditions().isEmpty();
} Try / catch
try {
String script = TerminationCompiler.buildTerminationScript(config);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("must have at least one sub-condition")) {
// add sub-conditions to the composite's conditions list
}
throw e;
} Prevention
- Always populate conditions when type is 'and' or 'or'.
- For single conditions, don't wrap in a composite.
- Validate composite termination configs in tests.
When it happens
Trigger: A TerminationConfig with type='and' or type='or' where config.getConditions() is null or returns an empty list.
Common situations: Setting up a composite termination but forgetting to populate the conditions list, or clearing it during config manipulation. Also happens when conditions are loaded from a list that was expected to be non-empty but came back empty.
Related errors
- Unknown termination type in composite: ${type}
- Composite termination must have at least one sub-condition
- Unknown termination type in nested 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/93c4f288c168f553.
Report an issue: GitHub.