{"record":{"id":"d8816bf2ad0a9ba7","repo":"google-gemini/gemini-cli","slug":"operation-cancelled","errorCode":null,"errorMessage":"Operation cancelled.","messagePattern":"Operation cancelled\\.","errorType":"exception","errorClass":"FatalCancellationError","httpStatus":null,"severity":"warning","filePath":"packages/cli/src/nonInteractiveCliAgentSession.ts","lineNumber":318,"sourceCode":"          content: input,\n        });\n      }\n\n      // Create LegacyAgentSession — owns the agentic loop\n      const session = new LegacyAgentSession({\n        client: geminiClient,\n        scheduler,\n        config,\n        promptId: prompt_id,\n      });\n\n      // Wire Ctrl+C to session abort\n      abortSession = () => {\n        void session.abort();\n      };\n      abortController.signal.addEventListener('abort', abortSession);\n      if (abortController.signal.aborted) {\n        throw new FatalCancellationError('Operation cancelled.');\n      }\n\n      // Start the agentic loop (runs in background)\n      const { streamId } = await session.send({\n        message: {\n          content: geminiPartsToContentParts(query),\n          displayContent: input,\n        },\n      });\n      if (streamId === null) {\n        throw new Error(\n          'LegacyAgentSession.send() unexpectedly returned no stream for a message send.',\n        );\n      }\n\n      const getTextContent = (parts?: ContentPart[]): string | undefined => {\n        const text = parts\n          ?.map((part) => (part.type === 'text' ? part.text : ''))","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/nonInteractiveCliAgentSession.ts#L300-L336","documentation":"Thrown immediately after wiring Ctrl+C to session.abort() when abortController.signal.aborted is already true at that point. It is a FatalCancellationError (exit code 130, the SIGINT convention). The operation was cancelled before the agentic loop's send() began.","triggerScenarios":"An abort was requested (Ctrl+C or programmatic abortController.abort()) in the window between addEventListener('abort') and the session.send() call, so the signal is already aborted when the guard runs.","commonSituations":"User pressed Ctrl+C very early in a non-interactive run; a parent process/signaller fired abort during startup; double Ctrl+C where the first abort landed before the guard check.","solutions":["This is expected cancellation behavior — no fix needed unless it fires without user intent.","If spurious, ensure no external code is calling abortController.abort() during session bootstrap.","Treat exit code 130 as a clean cancel rather than a crash in wrappers/CI."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAbortSignal(v: unknown): v is AbortSignal {\n  return v instanceof AbortSignal;\n}","tryCatchPattern":"try {\n  await runAgentSession(input);\n} catch (e) {\n  if (e instanceof FatalCancellationError) {\n    // exit code 130: clean cancel, do not treat as a failure in CI\n    process.exit(130);\n  }\n  throw e;\n}","preventionTips":["Treat exit code 130 as a normal cancellation, not a crash, in wrappers/CI.","Avoid calling abortController.abort() during session bootstrap unless intentional.","Forward SIGINT cleanly to the CLI rather than killing the process abruptly."],"tags":["cancellation","abort","non-interactive","sigint","agent-session"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}