github/copilot-sdk · error · InvalidOperationException

Failed to deserialize permission request

Error message

Failed to deserialize permission request

What it means

HandlePermissionRequestAsync receives permission-request payload data from the host and deserializes it into a PermissionRequest via SessionEventsJsonContext. If the JSON cannot be deserialized (shape mismatch, missing required fields) the result is null, which the code treats as a hard invariant failure and throws.

Solutions

  1. Upgrade the host/CLI and the SDK to matching versions so the PermissionRequest schema lines up.
  2. Log permissionRequestData.GetRawText() to inspect the offending payload and identify the schema mismatch.
  3. Catch InvalidOperationException in the permission-request event handler and fall back to PermissionDecision.UserNotAvailable().
  4. If you implement a host, validate permission payloads against the SDK's PermissionRequest contract.

Example fix

// before
try { OnPermissionRequest += async e => await DecideAsync(e); }

// after
try { var req = e.Request; }
catch (InvalidOperationException ex) { Log(ex, e.RawData); return PermissionDecision.UserNotAvailable(); }
Defensive patterns

Strategy: try-catch

Validate before calling

if (permissionRequestData.ValueKind != JsonValueKind.Object)
    return PermissionDecision.UserNotAvailable();

Type guard

static bool IsDeserializable(JsonElement e) =>
    e.ValueKind == JsonValueKind.Object && e.TryGetProperty("toolCall", out _);

Try / catch

try { var request = JsonSerializer.Deserialize(raw, SessionEventsJsonContext.Default.PermissionRequest); }
catch (InvalidOperationException ex) when (ex.Message == "Failed to deserialize permission request")
{ Log(raw); return PermissionDecision.UserNotAvailable(); }

Prevention

When it happens

Trigger: The host sends a permission request (e.g. for a tool call) whose `permissionRequestData` JSON does not match the PermissionRequest schema — wrong field names, wrong types, or an empty payload.

Common situations: Version mismatch between host (Copilot CLI/extension) and this SDK causing schema drift; a custom host emitting non-conforming permission payloads; corrupted or truncated JSON-RPC params.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at dotnet/src/Session.cs:639

        _mcpAuthHandler = handler;
    }

    /// <summary>
    /// Handles a permission request from the Copilot CLI.
    /// </summary>
    /// <param name="permissionRequestData">The permission request data from the CLI.</param>
    /// <returns>A task that resolves with the permission decision.</returns>
    internal async Task<PermissionDecision> HandlePermissionRequestAsync(JsonElement permissionRequestData)
    {
        var handler = _permissionHandler;

        if (handler == null)
        {
            return PermissionDecision.UserNotAvailable();
        }

        var request = JsonSerializer.Deserialize(permissionRequestData.GetRawText(), SessionEventsJsonContext.Default.PermissionRequest)
            ?? throw new InvalidOperationException("Failed to deserialize permission request");

        var invocation = new PermissionInvocation
        {
            SessionId = SessionId,
            ManagedSettingsEnabled = _managedSettingsEnabled
        };

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

    /// <summary>
    /// Handles broadcast request events by executing local handlers and responding via RPC.

View on GitHub (pinned to cd8cf15dc3)