{"record":{"id":"e9a3575a5b665b58","repo":"tinyhumansai/openhuman","slug":"runtime-unavailable-runtime-runtime-runtime","errorCode":null,"errorMessage":"Runtime unavailable: ${runtime.runtime} (${runtime.error ?? defaultReason})","messagePattern":"Runtime unavailable: (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/components/skills/WorkflowRunnerBody.tsx","lineNumber":566,"sourceCode":"    try {\n      await skillsApi.cancelRun(runId);\n    } catch (err) {\n      log('cancelRun error: %s', err instanceof Error ? err.message : String(err));\n    }\n    setRecentRunsRefreshNonce(n => n + 1);\n  }, []);\n\n  const ensureRuntimeAvailability = useCallback(async () => {\n    const runtimeRequirement = inferRuntimeRequirement(selectedWorkflow);\n    if (!runtimeRequirement) return;\n\n    const resolved = await skillsApi.resolveRuntimes(runtimeRequirement);\n    const unavailable = resolved.runtimes.filter(runtime => !runtime.available);\n    if (unavailable.length === 0) return;\n\n    const prefix = t('settings.skillsRunner.error.runtimeUnavailable', 'Runtime unavailable');\n    const defaultReason = t('settings.skillsRunner.error.runtimeUnavailableDefault', 'unavailable');\n    throw new Error(\n      unavailable\n        .map(runtime => `${prefix}: ${runtime.runtime} (${runtime.error ?? defaultReason})`)\n        .join('; ')\n    );\n  }, [selectedWorkflow, t]);\n\n  const handleRun = useCallback(async () => {\n    if (!description) return;\n    // Re-entry guard: a second click before React applies the disabled state\n    // would otherwise fire `skill_runtime_run` twice and spawn two real runs.\n    if (runSubmitGuardRef.current) {\n      log('runWorkflow: ignoring re-entrant click while a run is starting');\n      return;\n    }\n    if (missingRequired.length > 0) {\n      setRun({\n        status: 'error',\n        message: `${t('settings.skillsRunner.error.missingRequired')} ${missingRequired.join(', ')}`,","sourceCodeStart":548,"sourceCodeEnd":584,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/components/skills/WorkflowRunnerBody.tsx#L548-L584","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before - only discover unavailability when Run is clicked\nawait ensureRuntimeAvailability();\n\n// after - check up front and disable Run with a hint\nconst req = inferRuntimeRequirement(selectedWorkflow);\nconst resolved = req ? await skillsApi.resolveRuntimes(req) : null;\nconst missing = resolved?.runtimes.filter(r => !r.available) ?? [];\n// <RunButton disabled={missing.length > 0} title={missing.map(r => r.error).join('; ')} />","handlingStrategy":"validation","validationCode":"// Resolve runtimes when the workflow is selected, not when Run is clicked\nconst req = inferRuntimeRequirement(selectedWorkflow);\nconst resolved = req ? await skillsApi.resolveRuntimes(req) : null;\nconst missing = resolved?.runtimes.filter(r => !r.available) ?? [];\nif (missing.length > 0) {\n  setRuntimeWarning(missing.map(r => `${r.runtime}: ${r.error ?? 'unavailable'}`).join('; '));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["skills","workflow-runner","runtime-availability","node","python","pre-flight"],"backgroundTag":"missing-runtime-dependency","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}