Hmbown/CodeWhale · error · Error

Required Workflow result unavailable: ' + id

Error message

Required Workflow result unavailable: ' + id

What it means

The `key` tool resolver, when given a `duration`, converts the call into a `hold_key` wire tool. A held key occupies the keyboard for its whole duration, so modifiers like `repeat` or a `target` window cannot be honored concurrently — the library rejects the combination up front rather than silently dropping one of the options.

Solutions

  1. Remove `repeat` and `target` from the args when `duration` is set — the key is held for `duration` seconds and repeated pressing is not supported.
  2. If you need repeated presses, drop `duration` so it resolves as a plain `key` and issue multiple calls, or call `hold_key` directly and loop yourself.
  3. If you need the key delivered to a specific window, drop `duration` and use `target`, or start a trajectory/window-focus step before holding.

Example fix

// before
resolveTool("key", { key: "Shift", duration: 2, repeat: 3 });
// after
resolveTool("key", { key: "Shift", duration: 2 }); // or drop duration to keep repeat
Defensive patterns

Strategy: validation

Validate before calling

if (args.duration != null && (args.repeat != null || args.target != null)) {
  throw new Error("key: drop repeat/target when duration (hold_key) is set");
}

Type guard

const isHoldKeyArgs = (a) => a.duration != null && a.repeat == null && a.target == null;

Prevention

When it happens

Trigger: Calling resolveTool with tool "key" and args containing a non-null `duration` together with a non-null `repeat` OR a non-null `target`, e.g. key {key:"a", duration:1, repeat:3} or key {key:"Tab", duration:0.5, target:"editor"}.

Common situations: Models or scripts conflating the tap-style key tool with the hold-style hold_key tool; copying a key spec that includes repeat count and adding a hold duration to slow it down; migrating prompts from another computer-use API where held keys still support per-window targeting.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/64dd84e3955ce530. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/workflow/mod.rs:3450

            spec.id
        )));
    }
    Ok(())
}

fn leaf_description_expression(spec: &LeafSpec) -> String {
    let description = js_string(&leaf_description(spec));
    if spec.depends_on_results.is_empty() {
        return description;
    }
    let inputs = result_inputs_expression(&spec.depends_on_results);
    // parallel() retains null for a failed slot. A dependent worker must
    // not start with an empty stand-in for that result; reducers can still
    // inspect partial fan-out through their separate input projection.
    let required =
        serde_json::to_string(&spec.depends_on_results).expect("dependency IDs serialize to JSON");
    format!(
        "({required}.forEach(id => {{ if (__results[id] == null) throw new Error('Required Workflow result unavailable: ' + id); }}), {description} + \"\\n\\nInputs:\\n\" + {inputs})"
    )
}

fn result_inputs_expression(inputs: &[String]) -> String {
    let entries = inputs
        .iter()
        .map(|input| format!("[{}, __results[{}]]", js_string(input), js_string(input)))
        .collect::<Vec<_>>()
        .join(", ");
    format!(
        "[{entries}].map(([id, value]) => \"--- \" + id + \" ---\\n\" + String(value ?? \"\")).join(\"\\n\\n\")"
    )
}

fn leaf_subagent_type(spec: &LeafSpec) -> Option<&'static str> {
    // A named Fleet profile owns the child's runtime type. Emitting the IR's
    // default `general` here makes role-only leaves look like an explicit type
    // override and can conflict with the resolved roster member (for example,

View on GitHub (pinned to 73e0f67d83)