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
- Upgrade the host/CLI and the SDK to matching versions so the PermissionRequest schema lines up.
- Log permissionRequestData.GetRawText() to inspect the offending payload and identify the schema mismatch.
- Catch InvalidOperationException in the permission-request event handler and fall back to PermissionDecision.UserNotAvailable().
- 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
- Keep host/CLI and SDK versions in sync
- Log raw permission payloads when debugging schema drift
- Always provide a fallback decision for undecodable requests
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
- Expected a string token when reading
- Expected a non-empty string token when reading
- Expected string for ToolBinaryResultType.
- ToolBinaryResultType value cannot be null.
- Unknown MessageSource value
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)