{"record":{"id":"dcc8bf654f3d3100","repo":"slopus/happy","slug":"gemini-backend-or-session-not-initialized","errorCode":null,"errorMessage":"Gemini backend or session not initialized","messagePattern":"Gemini backend or session not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/gemini/runGemini.ts","lineNumber":1068,"sourceCode":"          throw new Error('ACP session not started');\n        }\n         \n        // Reset accumulator when sending a new prompt (not when tool calls start)\n        // Reset accumulated response for new prompt\n        // This ensures a new assistant message will be created (not updating previous one)\n        accumulatedResponse = '';\n        isResponseInProgress = false;\n        hadToolCallInTurn = false;\n        taskStartedSent = false; // Reset so new turn can send task_started\n        \n        // Track if this prompt contains change_title instruction\n        // If so, don't send task_complete until change_title is completed\n        pendingChangeTitle = message.message.includes('change_title') || \n                             message.message.includes('happy__change_title');\n        changeTitleCompleted = false;\n        \n        if (!geminiBackend || !acpSessionId) {\n          throw new Error('Gemini backend or session not initialized');\n        }\n        \n        // The prompt already includes system prompt and change_title instruction (added in onUserMessage handler)\n        // This is done in the message queue, so message.message already contains everything\n        let promptToSend = message.message;\n        \n        // Inject conversation history context if model was just changed\n        if (injectHistoryContext && conversationHistory.hasHistory()) {\n          const historyContext = conversationHistory.getContextForNewSession();\n          promptToSend = historyContext + promptToSend;\n          logger.debug(`[gemini] Injected conversation history context (${historyContext.length} chars)`);\n          // Don't clear history - keep accumulating for future model changes\n        }\n        \n        logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptToSend.length}): ${promptToSend.substring(0, 100)}...`);\n        logger.debug(`[gemini] Full prompt: ${promptToSend}`);\n        \n        // Retry logic for transient Gemini API errors (empty response, internal errors)","sourceCodeStart":1050,"sourceCodeEnd":1086,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/gemini/runGemini.ts#L1050-L1086","documentation":"Before sending a queued message (task_complete flow), runGemini re-validates that the Gemini backend object and the ACP session ID both exist. If either is missing the message cannot be forwarded to the agent, so it throws. This typically means the backend was never started or already shut down when a queued message was processed.","triggerScenarios":"The message queue processes a message containing 'change_title'/'happy__change_title' while geminiBackend is null (never initialized or disposed) or acpSessionId is unset.","commonSituations":"Race where the Gemini process exits mid-run and a queued message (e.g. automatic title change) is still processed; calling the task-complete path before runGemini finished initialization; backend cleanup on shutdown firing before the queue drains.","solutions":["Ensure the Gemini backend/session is fully initialized before any message is queued","Guard the queue drain so it stops when the backend is disposed, and clear pending messages on backend exit","Check logs for an earlier crash of the gemini ACP process that left geminiBackend/acpSessionId stale"],"exampleFix":"// before\nawait sendMessage(message.message);\n// after\nif (!geminiBackend || !acpSessionId) {\n  logger.warn('[gemini] Skipping message: backend already shut down');\n  return;\n}\nawait sendMessage(message.message);","handlingStrategy":"try-catch","validationCode":"if (!geminiBackend || !acpSessionId) {\n  logger.warn('[gemini] backend/session gone; dropping queued message');\n  return;\n}","typeGuard":"const backendReady = (b: GeminiBackend | null, s: string | null): b is GeminiBackend =>\n  b !== null && typeof s === 'string' && s.length > 0;","tryCatchPattern":"try {\n  await queue.drain();\n} catch (err) {\n  if (err instanceof Error && err.message.includes('not initialized')) {\n    queue.clear(); // backend shut down; drop pending messages\n  } else throw err;\n}","preventionTips":["Clear the message queue when the Gemini backend exits or is disposed","Do not queue messages before the ACP session is established","Listen to backend exit events and stop producers immediately"],"tags":["gemini","acp","session","lifecycle"],"backgroundTag":"session-not-initialized","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}