github/copilot-sdk · error · InvalidOperationException

No user input handler registered

Error message

No user input handler registered

What it means

HandleUserInputRequestAsync processes user-input requests sent by the Copilot CLI. It requires an application-registered user input handler (_userInputHandler); if none was registered when the CLI asks for user input, the SDK throws InvalidOperationException rather than silently returning an empty answer.

Solutions

  1. Register a user input handler on the session (the API that assigns _userInputHandler) before starting/continuing tasks.
  2. Implement the handler to surface the prompt to your UI/CLI and return a UserInputResponse.
  3. If interaction is impossible, ensure the agent flow avoids user-input-requiring tools, or catch the error and respond with a decline/failure to the request.
  4. Confirm the handler is registered on the same session instance that is running the task.

Example fix

// before
var session = new CopilotSession(...);
await session.SendAsync("deploy"); // throws on user input request

// after
var session = new CopilotSession(...);
session.RegisterUserInputHandler(async req => new UserInputResponse { Answer = Console.ReadLine() });
await session.SendAsync("deploy");
Defensive patterns

Strategy: validation

Validate before calling

if (!session.HasUserInputHandler)
    session.RegisterUserInputHandler(req => Task.FromResult(new UserInputResponse { Answer = AskUser(req) }));

Type guard

bool ready = session is { _userInputHandler: not null }; // prefer a public HasUserInputHandler check

Try / catch

try { var resp = await session.HandleUserInputRequestAsync(request); }
catch (InvalidOperationException ex) when (ex.Message == "No user input handler registered")
{ return UserInputResponse.Decline("Interactive input unavailable"); }

Prevention

When it happens

Trigger: The host sends a UserInputRequest (e.g. the agent needs user confirmation/input) while the app never called the registration API that sets _userInputHandler, or the handler was registered on a different session instance.

Common situations: Running the SDK without wiring the user-input callback while the agent task requires interaction; forgetting to register handlers after recreating the session; interactive flows exercised in headless test runs.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/cfe19244613c453c. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/Session.cs:1682

            if (result.Action == UIElicitationResponseAction.Accept
                && result.Content != null
                && result.Content.TryGetValue("value", out var val))
            {
                return val.ValueKind == JsonValueKind.String ? val.GetString() : val.ToString();
            }

            return null;
        }
    }

    /// <summary>
    /// Handles a user input request from the Copilot CLI.
    /// </summary>
    /// <param name="request">The user input request from the CLI.</param>
    /// <returns>A task that resolves with the user's response.</returns>
    internal async Task<UserInputResponse> HandleUserInputRequestAsync(UserInputRequest request)
    {
        var handler = _userInputHandler ?? throw new InvalidOperationException("No user input handler registered");
        var invocation = new UserInputInvocation
        {
            SessionId = SessionId
        };

        var userInputTimestamp = Stopwatch.GetTimestamp();
        var response = await handler(request, invocation);
        LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null,
            "CopilotSession.HandleUserInputRequestAsync dispatch. Elapsed={Elapsed}, SessionId={SessionId}",
            userInputTimestamp,
            SessionId);
        return response;
    }

    /// <summary>
    /// Handles an exit-plan-mode request from the Copilot CLI.
    /// </summary>
    /// <param name="request">The exit-plan-mode request from the CLI.</param>

View on GitHub (pinned to cd8cf15dc3)