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
- Read the parenthesized reason for each listed runtime - it distinguishes 'not installed' from version/binary problems
- Install/repair the runtime (Settings -> skills runner / let runtime_node bootstrap download the pinned toolchain), then retry
- Verify with the resolveRuntimes response that available flips to true before re-running
- 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
- Pre-install managed runtimes before users need them - resolveRuntimes is cheap to call on selection
- Read runtime.error per entry - it distinguishes 'not installed' from 'wrong version'
- Disable the Run button while any required runtime is unavailable to avoid the mid-click failure
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
- ` ` was not found. This MCP server needs uv (Python), which…
- Failed to fetch repositories
- Failed to fetch repositories
- Failed to list branches
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)