Skip to content

Commit 844d1b9

Browse files
committed
fix(viewer): singleton guard prevents port drift on reactivation
When the host gateway restarts and re-calls activate(), the previous ViewerServer instance may still hold the port. Without cleanup, the new instance hits EADDRINUSE and drifts to 18800+, causing Memory unavailable. - Add module-level singleton tracking for ViewerServer - Stop previous viewer instance before creating new one in activate() - Ensure server.stop() fully releases the HTTP port Fixes #1430
1 parent 96a1dd6 commit 844d1b9

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

apps/memos-local-openclaw/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ import { MEMORY_GUIDE_SKILL_MD } from "./src/skill/bundled-memory-guide";
3333
import { Telemetry } from "./src/telemetry";
3434

3535

36+
// Module-level singleton tracking for viewer/hub to prevent port drift on re-registration
37+
let _previousViewer: ViewerServer | null = null;
38+
let _previousHubServer: HubServer | null = null;
39+
let _previousStore: import('./src/storage/sqlite').SqliteStore | null = null;
40+
3641
/** Remove near-duplicate hits based on summary word overlap (>70%). Keeps first (highest-scored) hit. */
3742
function deduplicateHits<T extends { summary: string }>(hits: T[]): T[] {
3843
const kept: T[] = [];
@@ -2365,6 +2370,20 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
23652370

23662371
const derivedHubPort = gatewayPort + 11;
23672372

2373+
// Cleanup previous instances to prevent port drift (issue #1430)
2374+
if (_previousViewer) {
2375+
try { _previousViewer.stop(); } catch (e) { api.logger.warn(`memos-local: previous viewer cleanup: ${e}`); }
2376+
_previousViewer = null;
2377+
}
2378+
if (_previousHubServer) {
2379+
try { _previousHubServer.stop(); } catch (e) { api.logger.warn(`memos-local: previous hub cleanup: ${e}`); }
2380+
_previousHubServer = null;
2381+
}
2382+
if (_previousStore) {
2383+
try { _previousStore.close(); } catch (e) { api.logger.warn(`memos-local: previous store cleanup: ${e}`); }
2384+
_previousStore = null;
2385+
}
2386+
23682387
const viewer = new ViewerServer({
23692388
store,
23702389
embedder,
@@ -2378,6 +2397,10 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
23782397
? new HubServer({ store, log: ctx.log, config: ctx.config, dataDir: stateDir, embedder, defaultHubPort: derivedHubPort })
23792398
: null;
23802399

2400+
_previousViewer = viewer;
2401+
_previousHubServer = hubServer;
2402+
_previousStore = store;
2403+
23812404
// ─── Service lifecycle ───
23822405

23832406
let serviceStarted = false;

apps/memos-local-openclaw/src/viewer/server.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,18 @@ export class ViewerServer {
211211
}
212212
}
213213

214-
stop(): void {
214+
stop(): Promise<void> {
215215
this.stopHubHeartbeat();
216216
this.stopNotifPoll();
217217
for (const c of this.notifSSEClients) { try { c.end(); } catch {} }
218218
this.notifSSEClients = [];
219-
this.server?.close();
220-
this.server = null;
219+
if (!this.server) return Promise.resolve();
220+
return new Promise<void>((resolve) => {
221+
this.server!.close(() => {
222+
this.server = null;
223+
resolve();
224+
});
225+
});
221226
}
222227

223228
getResetToken(): string {

0 commit comments

Comments
 (0)