{"record":{"id":"9fce2fdcb9b348d1","repo":"github/copilot-sdk","slug":"joinsession-is-intended-for-extensions-running-a","errorCode":null,"errorMessage":"joinSession() is intended for extensions running as child processes of the Copilot CLI.","messagePattern":"joinSession\\(\\) is intended for extensions running as child processes of the Copilot CLI\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/extension.ts","lineNumber":114,"sourceCode":"} from \"./factory.js\";\n\n/**\n * Joins the current foreground session.\n *\n * @param config - Configuration to add to the session\n * @returns A promise that resolves with the joined session\n *\n * @example\n * ```typescript\n * import { joinSession } from \"@github/copilot-sdk/extension\";\n *\n * const session = await joinSession({ tools: [myTool] });\n * ```\n */\nexport async function joinSession(config: JoinSessionConfig = {}): Promise<CopilotSession> {\n    const sessionId = process.env.SESSION_ID;\n    if (!sessionId) {\n        throw new Error(\n            \"joinSession() is intended for extensions running as child processes of the Copilot CLI.\"\n        );\n    }\n\n    const client = new CopilotClient({ _internalConnection: { kind: \"parent-process\" } });\n\n    // Strip `extensionSdkPath` at runtime even though `JoinSessionConfig` omits it\n    // at the type level — untyped (JS) callers can still slip it through, and\n    // honoring it here would be misleading since the extension subprocess has\n    // already been forked by the host with the SDK the host chose.\n    const {\n        extensionSdkPath: _stripped,\n        factories,\n        requestedEnvironmentVariables,\n        ...rest\n    } = config as JoinSessionConfig & {\n        extensionSdkPath?: string;\n    };","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/extension.ts#L96-L132","documentation":"joinSession() connects an extension back to the Copilot CLI session that spawned it. That link is only meaningful for child processes, which the CLI identifies via the SESSION_ID environment variable it injects. Without SESSION_ID there is no parent session to join, so the call fails immediately.","triggerScenarios":"Calling joinSession() from a normal shell, script, CI job, or any process not launched as a child process of the Copilot CLI (SESSION_ID unset).","commonSituations":"Running an extension entry point directly with node/ts-node during development; calling joinSession() in CI; confusing joinSession() with CopilotClient-based session creation for standalone apps.","solutions":["For standalone apps, create a session explicitly with CopilotClient instead of joinSession().","Run the extension through the Copilot CLI so it is spawned as a child process with SESSION_ID set.","In development, launch the extension via the CLI extension mechanism rather than invoking the file directly.","Optionally check process.env.SESSION_ID first and fall back to a CopilotClient flow when absent."],"exampleFix":"// before\nconst session = await joinSession({ tools }); // throws outside CLI child process\n// after\nif (!process.env.SESSION_ID) {\n  const client = new CopilotClient();\n  var session = await client.createSession({ tools });\n} else {\n  var session = await joinSession({ tools });\n}","handlingStrategy":"validation","validationCode":"if (!process.env.SESSION_ID) {\n  throw new Error('Not running as a Copilot CLI child process; use CopilotClient instead of joinSession()');\n}\nconst session = await joinSession(config);","typeGuard":"const runsInsideCopilotCli = (): boolean =>\n  typeof process.env.SESSION_ID === 'string' && process.env.SESSION_ID.length > 0;","tryCatchPattern":"try {\n  const session = await joinSession(config);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('child processes of the Copilot CLI')) {\n    const client = new CopilotClient();\n    const session = await client.createSession(config);\n  } else throw e;\n}","preventionTips":["Call joinSession() only from processes spawned by the Copilot CLI.","Check SESSION_ID before calling and fall back to CopilotClient for standalone runs.","Use CopilotClient.createSession() for apps and CI, not joinSession().","Document that extensions must be launched via the CLI extension mechanism."],"tags":["environment","cli","usage"],"backgroundTag":"missing-env-var","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}