From ff3959b734ef9c1ff2fef2fd527de55340cee76b Mon Sep 17 00:00:00 2001 From: isaacclad Date: Fri, 26 Jun 2026 22:09:45 +0000 Subject: [PATCH] Fix browser terminal falsely reporting 'failed to start' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createSession only waited 2.5s for the worker to flip starting->running, but the worker cold-boots the framework (php artisan) + opens the shell, which under load exceeds that. On timeout it also overwrote the (healthy) worker's metadata to status=failed, so customers saw 'Terminal worker failed to start' even though the shell was live. Wait up to 8s and return early once status is known; never fabricate a failure — the worker records real boot errors itself. Co-Authored-By: Claude Opus 4.8 --- .../Hosting/BrowserTerminalSessionManager.php | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/app/Services/Hosting/BrowserTerminalSessionManager.php b/app/Services/Hosting/BrowserTerminalSessionManager.php index fb1e57f..ad7cb70 100644 --- a/app/Services/Hosting/BrowserTerminalSessionManager.php +++ b/app/Services/Hosting/BrowserTerminalSessionManager.php @@ -10,6 +10,11 @@ class BrowserTerminalSessionManager { private const IDLE_TIMEOUT_SECONDS = 120; + // How long createSession() waits for the worker to cold-boot the framework + // and open the shell before returning. Generous so a slow/loaded boot isn't + // mistaken for a failure; the wait returns early once the status is known. + private const STARTUP_TIMEOUT_SECONDS = 8.0; + public function createSession(HostingAccount $account, int $userId, string $relativePath = '/public_html', int $cols = 120, int $rows = 30): array { $sessionId = (string) Str::uuid(); @@ -334,7 +339,9 @@ class BrowserTerminalSessionManager private function waitForWorkerStartup(string $sessionId, array $metadata): array { - $deadline = microtime(true) + 2.5; + // The loop returns the moment status leaves "starting" (running OR a + // genuine error the worker recorded), so a fast boot still returns fast. + $deadline = microtime(true) + self::STARTUP_TIMEOUT_SECONDS; while (microtime(true) < $deadline) { usleep(100000); @@ -346,28 +353,11 @@ class BrowserTerminalSessionManager } } - $metadata['status'] = 'failed'; - $metadata['exit_code'] = 1; - $metadata['closed_at'] = now()->toIso8601String(); - $metadata['error'] = $this->workerBootstrapError($sessionId); - $this->writeMetadata($sessionId, $metadata); - - return $metadata; - } - - private function workerBootstrapError(string $sessionId): string - { - $output = trim((string) @file_get_contents($this->outputLogPath($sessionId))); - - if ($output !== '') { - $lines = preg_split("/\r\n|\n|\r/", $output) ?: []; - $tail = trim((string) end($lines)); - - if ($tail !== '') { - return 'Terminal worker failed to start: '.$tail; - } - } - - return 'Terminal worker failed to start.'; + // Still starting after the wait: do NOT fabricate a failure — that also + // clobbered workers that were about to come up, which is exactly what made + // healthy terminals report "failed to start". Return current metadata; the + // browser keeps polling and the worker writes a real error itself if boot + // ultimately fails. + return $this->readWorkerMetadata($sessionId); } }