Runtime issues: monitorServerLogs() hangs, and appData is undefined in layout (likely due to SvelteKit plugin bug) #26

Closed
opened 2026-02-15 20:16:51 -05:00 by yindo · 0 comments
Owner

Originally created by @AmesianX on GitHub (Apr 3, 2025).

The monitorServerLogs function has a bug that causes it to hang without returning a value.
After modifying the source code as shown below, the application initially works correctly, but then it halts again, stating that the appData variable is undefined in the layout. Accessing the app directly through the browser works as expected, so this appears to be a serious issue with the SvelteKit plugin.

src\utils\index.ts

/**
 * Spawn the Open-WebUI server process.
 */
export async function startServer(
	installationPath?: string,
	expose = false,
	port = 8080
): Promise<string> {
	installationPath = path.normalize(installationPath || getBundledPythonInstallationPath());

	if (!(await validateInstallation(installationPath))) {
		console.error('Failed to validate installation');
		logEmitter.emit('log', 'Failed to validate installation'); // Emit log
		return;
	}

	try {
		const bundledPythonPath = getBundledPythonPath();

		// Execute the Python binary to print the version
		const pythonVersion = execFileSync(bundledPythonPath, ['--version'], {
			encoding: 'utf-8'
		});
		console.log('Installed Python Version:', pythonVersion.trim());
		logEmitter.emit('log', `Installed Python Version: ${pythonVersion.trim()}`); // Emit log
	} catch (error) {
		log.error('Failed to execute Python binary', error);
	}

	const host = expose ? '0.0.0.0' : '127.0.0.1';

	// Windows HATES Typer-CLI used to create the CLI for Open-WebUI
	// So we have to manually create the command to start the server
	let startCommand =
		process.platform === 'win32'
			? `"${installationPath}\\Scripts\\activate.bat" && uvicorn open_webui.main:app --host "${host}" --forwarded-allow-ips '*'`
			: `source "${installationPath}/bin/activate" && open-webui serve --host "${host}"`;

	if (process.platform === 'win32') {
		process.env.FROM_INIT_PY = 'true';
	}

	// Set environment variables in a platform-agnostic way
	process.env.DATA_DIR = path.join(app.getPath('userData'), 'data');
	process.env.WEBUI_SECRET_KEY = getSecretKey();

	port = port || 8080;
	while (await portInUse(port)) {
		port++;
	}

	startCommand += ` --port ${port}`;

	console.log('Starting Open-WebUI server...', startCommand);
	logEmitter.emit('log', `${startCommand}`); // Emit log
	logEmitter.emit('log', 'Starting Open-WebUI server...'); // Emit log

	const childProcess = spawn(startCommand, {
		shell: true,
		detached: process.platform !== 'win32', // Detach the child process on Unix-like platforms
		stdio: ['ignore', 'pipe', 'pipe'] // Let us capture logs via stdout/stderr
	});

	let detectedURL: string | null = null;

	detectedURL = 'http://' + host + ':' + port;

	console.log(`Server is now running at ${detectedURL}`);
	logEmitter.emit('log', `Server is now running at ${detectedURL}`); // Emit server URL log
	return detectedURL; // Return the detected URL
}

src\main.ts

import {
	app,
	nativeImage,
	desktopCapturer,
	session,
	clipboard,
	shell,
	Tray,
	Menu,
	MenuItem,
	BrowserWindow,
	globalShortcut,
	Notification,
	ipcMain,
	ipcRenderer
} from 'electron';
// import 'net'
import net from 'net';
import path from 'path';
import started from 'electron-squirrel-startup';
	const startServerHandler = async () => {
		SERVER_STATUS = 'starting';
		mainWindow.webContents.send('main:data', {
			type: 'server:status',
			data: SERVER_STATUS
		});
		updateTrayMenu('Open WebUI: Starting...', null);

		try {
			SERVER_URL = await startServer();
			// SERVER_URL = 'http://localhost:5050';

			SERVER_STATUS = 'started';
			mainWindow.webContents.send('main:data', {
				type: 'server:status',
				data: SERVER_STATUS
			});

			function waitForServerReady(port: number, host = '127.0.0.1', timeout = 10000): Promise<void>
			{
				const start = Date.now();
			    return new Promise((resolve, reject) => {
			        const tryConnect = () => {
			            const socket = new net.Socket();
			            socket
			                .setTimeout(1000)
			                .once('connect', () => {
			                    socket.destroy();
			                    resolve();
			                })
			                .once('timeout', () => {
			                    socket.destroy();
			                    if (Date.now() - start > timeout) reject(new Error('Timeout'));
			                    else setTimeout(tryConnect, 300);
			                })
			                .once('error', () => {
			                    socket.destroy();
			                    if (Date.now() - start > timeout) reject(new Error('Timeout'));
			                    else setTimeout(tryConnect, 300);
			                })
			                .connect(port, host);
			        };

			        tryConnect();
			    });
			}

            // 사용 예:
            await waitForServerReady(8080);

			mainWindow.loadURL(SERVER_URL);
			mainWindow;

			const urlObj = new URL(SERVER_URL);
			const port = urlObj.port || '8080'; // Fallback to port 8080 if not provided
			updateTrayMenu(`Open WebUI: Running on port ${port}`, SERVER_URL); // Update tray menu with running status
		} catch (error) {
			console.error('Failed to start server:', error);
			SERVER_STATUS = 'failed';
			mainWindow.webContents.send('main:data', {
				type: 'server:status',
				data: SERVER_STATUS
			});

			mainWindow.webContents.send('main:log', `Failed to start server: ${error}`);
			updateTrayMenu('Open WebUI: Failed to Start', null); // Update tray menu with failure status
		}
	};
Originally created by @AmesianX on GitHub (Apr 3, 2025). The monitorServerLogs function has a bug that causes it to hang without returning a value. After modifying the source code as shown below, the application initially works correctly, but then it halts again, stating that the appData variable is undefined in the layout. Accessing the app directly through the browser works as expected, so this appears to be a serious issue with the SvelteKit plugin. ## src\utils\index.ts ``` /** * Spawn the Open-WebUI server process. */ export async function startServer( installationPath?: string, expose = false, port = 8080 ): Promise<string> { installationPath = path.normalize(installationPath || getBundledPythonInstallationPath()); if (!(await validateInstallation(installationPath))) { console.error('Failed to validate installation'); logEmitter.emit('log', 'Failed to validate installation'); // Emit log return; } try { const bundledPythonPath = getBundledPythonPath(); // Execute the Python binary to print the version const pythonVersion = execFileSync(bundledPythonPath, ['--version'], { encoding: 'utf-8' }); console.log('Installed Python Version:', pythonVersion.trim()); logEmitter.emit('log', `Installed Python Version: ${pythonVersion.trim()}`); // Emit log } catch (error) { log.error('Failed to execute Python binary', error); } const host = expose ? '0.0.0.0' : '127.0.0.1'; // Windows HATES Typer-CLI used to create the CLI for Open-WebUI // So we have to manually create the command to start the server let startCommand = process.platform === 'win32' ? `"${installationPath}\\Scripts\\activate.bat" && uvicorn open_webui.main:app --host "${host}" --forwarded-allow-ips '*'` : `source "${installationPath}/bin/activate" && open-webui serve --host "${host}"`; if (process.platform === 'win32') { process.env.FROM_INIT_PY = 'true'; } // Set environment variables in a platform-agnostic way process.env.DATA_DIR = path.join(app.getPath('userData'), 'data'); process.env.WEBUI_SECRET_KEY = getSecretKey(); port = port || 8080; while (await portInUse(port)) { port++; } startCommand += ` --port ${port}`; console.log('Starting Open-WebUI server...', startCommand); logEmitter.emit('log', `${startCommand}`); // Emit log logEmitter.emit('log', 'Starting Open-WebUI server...'); // Emit log const childProcess = spawn(startCommand, { shell: true, detached: process.platform !== 'win32', // Detach the child process on Unix-like platforms stdio: ['ignore', 'pipe', 'pipe'] // Let us capture logs via stdout/stderr }); let detectedURL: string | null = null; detectedURL = 'http://' + host + ':' + port; console.log(`Server is now running at ${detectedURL}`); logEmitter.emit('log', `Server is now running at ${detectedURL}`); // Emit server URL log return detectedURL; // Return the detected URL } ``` ## src\main.ts ``` import { app, nativeImage, desktopCapturer, session, clipboard, shell, Tray, Menu, MenuItem, BrowserWindow, globalShortcut, Notification, ipcMain, ipcRenderer } from 'electron'; // import 'net' import net from 'net'; import path from 'path'; import started from 'electron-squirrel-startup'; ``` ``` const startServerHandler = async () => { SERVER_STATUS = 'starting'; mainWindow.webContents.send('main:data', { type: 'server:status', data: SERVER_STATUS }); updateTrayMenu('Open WebUI: Starting...', null); try { SERVER_URL = await startServer(); // SERVER_URL = 'http://localhost:5050'; SERVER_STATUS = 'started'; mainWindow.webContents.send('main:data', { type: 'server:status', data: SERVER_STATUS }); function waitForServerReady(port: number, host = '127.0.0.1', timeout = 10000): Promise<void> { const start = Date.now(); return new Promise((resolve, reject) => { const tryConnect = () => { const socket = new net.Socket(); socket .setTimeout(1000) .once('connect', () => { socket.destroy(); resolve(); }) .once('timeout', () => { socket.destroy(); if (Date.now() - start > timeout) reject(new Error('Timeout')); else setTimeout(tryConnect, 300); }) .once('error', () => { socket.destroy(); if (Date.now() - start > timeout) reject(new Error('Timeout')); else setTimeout(tryConnect, 300); }) .connect(port, host); }; tryConnect(); }); } // 사용 예: await waitForServerReady(8080); mainWindow.loadURL(SERVER_URL); mainWindow; const urlObj = new URL(SERVER_URL); const port = urlObj.port || '8080'; // Fallback to port 8080 if not provided updateTrayMenu(`Open WebUI: Running on port ${port}`, SERVER_URL); // Update tray menu with running status } catch (error) { console.error('Failed to start server:', error); SERVER_STATUS = 'failed'; mainWindow.webContents.send('main:data', { type: 'server:status', data: SERVER_STATUS }); mainWindow.webContents.send('main:log', `Failed to start server: ${error}`); updateTrayMenu('Open WebUI: Failed to Start', null); // Update tray menu with failure status } }; ```
yindo closed this issue 2026-02-15 20:16:51 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: open-webui/desktop#26