conductor-oss/conductor · error · SafeConditionParseException

null condition

Error message

null condition

What it means

SafeConditionInterpreter.parse throws SafeConditionParseException when the supplied condition expression is null. The interpreter is a hand-written recursive-descent parser/evaluator for plan-supplied `success_condition` strings; a null input has no grammar to parse and is rejected up front rather than NPE-ing inside the lexer.

Source

Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/util/SafeConditionInterpreter.java:78

 *   <li>{@code constructor}, {@code __proto__}, {@code Function}, {@code eval}.
 * </ul>
 *
 * <p>If those ever need to be supported, extending the grammar requires adding a new node type with
 * a {@link Node#eval(Map)} implementation — the addition is auditable in code review rather than
 * silently re-opened by widening a regex.
 */
public final class SafeConditionInterpreter {

    private SafeConditionInterpreter() {}

    /** Maximum source length accepted by the parser. */
    public static final int MAX_LENGTH = 1024;

    // ── Public API ─────────────────────────────────────────────────

    /** Parse a condition into an AST. Throws on syntax errors. */
    public static Node parse(String src) {
        if (src == null) throw new SafeConditionParseException("null condition");
        if (src.length() > MAX_LENGTH) {
            throw new SafeConditionParseException(
                    "condition exceeds " + MAX_LENGTH + " characters");
        }
        Parser p = new Parser(src);
        Node ast = p.parseExpr();
        p.expectEnd();
        return ast;
    }

    /** Evaluate the parsed condition against a root map. */
    public static boolean evaluate(String src, Map<String, Object> root) {
        return truthy(parse(src).eval(root != null ? root : Map.of()));
    }

    /** True if and only if {@link #parse(String)} would accept this string. */
    public static boolean isSafe(String src) {
        try {

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Null-check before calling parse: treat null as "no condition" and skip evaluation.
  2. Default the success_condition field to a literal like `true` when unset.
  3. Validate the plan payload at ingest time and reject missing required conditions.

Example fix

// before
boolean ok = SafeConditionInterpreter.parse(condition).eval(root);  // condition == null
// after
boolean ok = condition == null || SafeConditionInterpreter.evaluate(condition, root);
Defensive patterns

Strategy: validation

Validate before calling

if (condition == null) {
    // no condition supplied -> treat as pass / skip
    return true;
}
return SafeConditionInterpreter.evaluate(condition, root);

Type guard

boolean isParsableCondition(String s) {
    return s != null && s.length() <= SafeConditionInterpreter.MAX_LENGTH;
}

Try / catch

try {
    return SafeConditionInterpreter.evaluate(condition, root);
} catch (SafeConditionParseException e) {
    // null condition is a plan/ingest bug; fail the step with a clear message
    throw new IllegalArgumentException("invalid success_condition: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling SafeConditionInterpreter.parse(null) or evaluate(null, root). A workflow/plan supplies a null success_condition that reaches the parser without a null guard.

Common situations: A plan step omits success_condition entirely and the caller does not skip the parse; a JSON deserialization yields null for an unset field; a default value was not applied.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/f9ea1a9d818e4424. Report an issue: GitHub.