tinyhumansai/openhuman · error · Error

Runtime unavailable: ( )

Error message

Runtime unavailable: ${runtime.runtime} (${runtime.error ?? defaultReason})

What it means

Pre-flight check before running a workflow from the UI: inferRuntimeRequirement(selectedWorkflow) determines which runtimes the workflow needs; skillsApi.resolveRuntimes(...) reports each one's availability, and every runtime with available:false is folded into one joined message 'Runtime unavailable: <name> (<error | unavailable-default>)' (localized prefix/default via settings.skillsRunner.error.*). The runtime.error field, when present, carries the concrete reason (e.g. runtime not installed, wrong version).

Solutions

  1. Read the parenthesized reason for each listed runtime - it distinguishes 'not installed' from version/binary problems
  2. Install/repair the runtime (Settings -> skills runner / let runtime_node bootstrap download the pinned toolchain), then retry
  3. Verify with the resolveRuntimes response that available flips to true before re-running
  4. If the runtime is intentionally absent, pick a workflow variant that does not require it

Example fix

// before - only discover unavailability when Run is clicked
await ensureRuntimeAvailability();

// after - check up front and disable Run with a hint
const req = inferRuntimeRequirement(selectedWorkflow);
const resolved = req ? await skillsApi.resolveRuntimes(req) : null;
const missing = resolved?.runtimes.filter(r => !r.available) ?? [];
// <RunButton disabled={missing.length > 0} title={missing.map(r => r.error).join('; ')} />
Defensive patterns

Strategy: validation

Validate before calling

// Resolve runtimes when the workflow is selected, not when Run is clicked
const req = inferRuntimeRequirement(selectedWorkflow);
const resolved = req ? await skillsApi.resolveRuntimes(req) : null;
const missing = resolved?.runtimes.filter(r => !r.available) ?? [];
if (missing.length > 0) {
  setRuntimeWarning(missing.map(r => `${r.runtime}: ${r.error ?? 'unavailable'}`).join('; '));
}

Prevention

When it happens

Trigger: Running a workflow whose steps need the managed Node.js and/or Python runtime while resolveRuntimes reports available:false - runtime never downloaded/installed by runtime_node bootstrap, install incomplete, wrong version, or the runtime binary missing from disk.

Common situations: Fresh install where the managed runtime never finished downloading; disk cleanup removed the runtime dir; workflow edited to add a node/python step for the first time; version pin mismatch after an app update.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/e9a3575a5b665b58. Report an issue: GitHub.

Appendix: source

Thrown at app/src/components/skills/WorkflowRunnerBody.tsx:566

    try {
      await skillsApi.cancelRun(runId);
    } catch (err) {
      log('cancelRun error: %s', err instanceof Error ? err.message : String(err));
    }
    setRecentRunsRefreshNonce(n => n + 1);
  }, []);

  const ensureRuntimeAvailability = useCallback(async () => {
    const runtimeRequirement = inferRuntimeRequirement(selectedWorkflow);
    if (!runtimeRequirement) return;

    const resolved = await skillsApi.resolveRuntimes(runtimeRequirement);
    const unavailable = resolved.runtimes.filter(runtime => !runtime.available);
    if (unavailable.length === 0) return;

    const prefix = t('settings.skillsRunner.error.runtimeUnavailable', 'Runtime unavailable');
    const defaultReason = t('settings.skillsRunner.error.runtimeUnavailableDefault', 'unavailable');
    throw new Error(
      unavailable
        .map(runtime => `${prefix}: ${runtime.runtime} (${runtime.error ?? defaultReason})`)
        .join('; ')
    );
  }, [selectedWorkflow, t]);

  const handleRun = useCallback(async () => {
    if (!description) return;
    // Re-entry guard: a second click before React applies the disabled state
    // would otherwise fire `skill_runtime_run` twice and spawn two real runs.
    if (runSubmitGuardRef.current) {
      log('runWorkflow: ignoring re-entrant click while a run is starting');
      return;
    }
    if (missingRequired.length > 0) {
      setRun({
        status: 'error',
        message: `${t('settings.skillsRunner.error.missingRequired')} ${missingRequired.join(', ')}`,

View on GitHub (pinned to 7491200858)