{"record":{"id":"7a8d5d585eb13c7f","repo":"unslothai/unsloth","slug":"installer-exited-returncode-tail-or-no-output","errorCode":null,"errorMessage":"installer exited {returncode}: {tail or 'no output'}","messagePattern":"installer exited (.+?): (.+?)","errorType":"exception","errorClass":"InstallerExit","httpStatus":null,"severity":"error","filePath":"studio/backend/utils/prebuilt/update_flow.py","lineNumber":414,"sourceCode":"                    announced.discard(child_pid)\n                continue\n            m = PROGRESS_LINE_RE.search(line)\n            if m is None:\n                continue\n            set_progress(min(float(m.group(1)) / 100.0, 1.0) * DOWNLOAD_PROGRESS_CEILING)\n        returncode = proc.wait()\n    finally:\n        watchdog.cancel()\n        if proc.poll() is not None:\n            forget_pid(proc.pid)\n        # Anything it started and never reported as stopped, whether it timed\n        # out, exited nonzero, or died mid-line.\n        _stop_announced()\n    if timed_out.is_set():\n        raise RuntimeError(f\"installer timed out after {timeout_seconds}s\")\n    if returncode != 0:\n        tail = \"\".join(tail_lines).strip()[-1500:]\n        raise InstallerExit(returncode, f\"installer exited {returncode}: {tail or 'no output'}\")\n\n\ndef _new_phase_record(spec: dict) -> dict:\n    \"\"\"Initial breakdown entry for one phase of a chained job.\"\"\"\n    runnable = spec.get(\"run\") is not None\n    return {\n        \"state\": PHASE_PENDING if runnable else PHASE_SKIPPED,\n        \"reason\": None if runnable else spec.get(\"skip_reason\"),\n        \"progress\": None,\n        \"to_tag\": None,\n        \"reload_required\": None,\n        \"message\": \"\",\n        \"error\": None,\n    }\n\n\ndef run_chained_update(phases: list[dict], *, job: dict, job_lock: threading.Lock) -> None:\n    \"\"\"Run update phases in order into one shared job dict (the worker of a","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/prebuilt/update_flow.py#L396-L432","documentation":"Raised as InstallerExit(returncode, message) when the prebuilt-installer subprocess finishes with a nonzero exit code. The message embeds the last ~1500 characters of captured installer output (tail_lines) so the underlying installer failure is visible. The finally block already cleans up: cancels the watchdog, unregisters the PID, and stops any components the installer announced but never reported stopped.","triggerScenarios":"The installer subprocess exits with returncode != 0 — failed integrity check of the downloaded artifact, unwritable target directory, version conflict, missing runtime dependency, or the installer's own internal error. The error message carries the installer's stderr/stdout tail for diagnosis.","commonSituations":"Corrupted or partially-downloaded installer payload, insufficient disk space or permissions at the install target, an already-running process locking files being replaced, or a version mismatch between the installer bootstrap and the runtime it expects.","solutions":["Read the embedded tail in the error message — it is the installer's own failure output and pinpoints the exact sub-step that failed.","Free disk space and verify write permissions on the install target directory.","Stop other studio processes that may hold locks on files being updated, then re-run the update.","If the tail shows a checksum/verification failure, clear the cached download and retry to re-fetch the artifact."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import shutil\n\ndef can_install(target_dir) -> tuple[bool, str]:\n    free = shutil.disk_usage(target_dir).free\n    if free < REQUIRED_BYTES:\n        return False, f'insufficient disk space: {free} bytes free'\n    if not os.access(target_dir, os.W_OK):\n        return False, 'target directory not writable'\n    return True, ''","typeGuard":null,"tryCatchPattern":"try:\n    run_update_flow(spec)\nexcept InstallerExit as e:\n    log.error('installer failed rc=%s: %s', e.returncode, e.message)\n    surface_to_user(e.message)  # tail contains the installer's own diagnosis\n    # do not blind-retry; fix the named cause first","preventionTips":["Pre-check disk space and write permissions on the install target before starting an update.","Stop dependent processes before updating so no file locks interrupt the installer.","Always read the embedded output tail — the installer's own message identifies the failing phase."],"tags":["installer","subprocess","update","python"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}