stop leaking the parent claude session into the one we spawn
pm2 inherits the environment of whoever ran pm2 start, so restarting this sidecar from inside a claude code terminal — which is how it is restarted most of the time — bakes that terminal's session into the daemon. right now this process is carrying CLAUDE_CODE_MESSAGING_SOCKET for an unrelated pid that has been alive for an hour and a half. three of these were already stripped; the rest arrived with 2.x and were never added. this is hygiene, not the fix for today's hang — a spawn was verified to succeed with the whole set present — but a child attaching to a stranger's ipc socket is not a failure anyone would recognise from the symptom. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,39 @@ function resolveClaudeBin(): string {
|
|||||||
const CLAUDE_BIN = resolveClaudeBin();
|
const CLAUDE_BIN = resolveClaudeBin();
|
||||||
console.log(`[claude] CLI resolved to ${CLAUDE_BIN}`);
|
console.log(`[claude] CLI resolved to ${CLAUDE_BIN}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variables the `claude` CLI injects to describe ITS OWN session, which must never reach a `claude` we
|
||||||
|
* spawn ourselves.
|
||||||
|
*
|
||||||
|
* They arrive here by an ordinary accident: PM2 inherits the environment of whoever ran `pm2 start`, so
|
||||||
|
* restarting this sidecar from inside a Claude Code terminal — which is how it is restarted most of the
|
||||||
|
* time — bakes that terminal's session into the daemon. On 2026-08-14 this process was carrying
|
||||||
|
* `CLAUDE_CODE_MESSAGING_SOCKET` for an unrelated PID that had been alive for an hour and a half.
|
||||||
|
*
|
||||||
|
* Only three of these were stripped before (`CLAUDECODE`, `CLAUDE_CODE_ENTRYPOINT`, `CLAUDE_CODE_SSE_PORT`);
|
||||||
|
* the rest are newer and arrived with 2.x. Stripping them is hygiene rather than a fix — a spawn was
|
||||||
|
* verified to succeed with the whole set present — but the failure it prevents is a child attaching to a
|
||||||
|
* stranger's IPC socket, which would be extremely hard to recognise from the symptom.
|
||||||
|
*/
|
||||||
|
const NESTED_SESSION_ENV = [
|
||||||
|
'CLAUDECODE',
|
||||||
|
'CLAUDE_CODE_ENTRYPOINT',
|
||||||
|
'CLAUDE_CODE_SSE_PORT',
|
||||||
|
'CLAUDE_CODE_CHILD_SESSION',
|
||||||
|
'CLAUDE_CODE_MESSAGING_SOCKET',
|
||||||
|
'CLAUDE_CODE_MESSAGING_TOKEN',
|
||||||
|
'CLAUDE_CODE_SESSION_ID',
|
||||||
|
'CLAUDE_CODE_EXECPATH',
|
||||||
|
'CLAUDE_PID',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/** `process.env` minus the parent session's fingerprint. */
|
||||||
|
function envWithoutParentSession(): Record<string, string> {
|
||||||
|
const out: Record<string, string> = { ...process.env } as Record<string, string>;
|
||||||
|
for (const name of NESTED_SESSION_ENV) delete out[name];
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Capture original HOME before user-instance overrides it
|
// Capture original HOME before user-instance overrides it
|
||||||
const HOST_HOME = process.env.HOME!;
|
const HOST_HOME = process.env.HOME!;
|
||||||
import { DATA_PATH } from '../../data-path';
|
import { DATA_PATH } from '../../data-path';
|
||||||
@@ -404,7 +437,7 @@ function createSession(params: ClaudeSpawnStreamingParams, onEvent: (event: Chat
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Strip the nested-session guard vars so the SDK can spawn `claude` (mirrors the old spawn env clean).
|
// Strip the nested-session guard vars so the SDK can spawn `claude` (mirrors the old spawn env clean).
|
||||||
const { CLAUDECODE: _c, CLAUDE_CODE_ENTRYPOINT: _e, CLAUDE_CODE_SSE_PORT: _s, ...cleanEnv } = process.env;
|
const cleanEnv = envWithoutParentSession();
|
||||||
|
|
||||||
const resumeId = getClaudeSession(sessionKey, params.userId) ?? params.resumeSessionId;
|
const resumeId = getClaudeSession(sessionKey, params.userId) ?? params.resumeSessionId;
|
||||||
const subModel = params.model?.split('/')[1];
|
const subModel = params.model?.split('/')[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user