github/copilot-sdk · error · IOException

Failed to write a frame to the in-process runtime…

Error message

Failed to write a frame to the in-process runtime connection.

What it means

CallbackSendStream.WriteFrame throws IOException when the frame-write callback (backed by copilot_runtime_connection_write) returns false, i.e. the native runtime refused or failed to accept the outgoing JSON-RPC frame on the in-process connection.

Solutions

  1. Check whether the FfiRuntimeHost/connection is still open before sending; stop and reconnect if not.
  2. Restart the runtime connection (new host via Create + StartAsync) after the write failure.
  3. Inspect native logs for the reason the write callback reported failure.
  4. Add an orderly Shutdown before process exit to avoid writes into a torn-down connection.
  5. Guard send paths so writes after a failed StartAsync are not attempted.

Example fix

// before
await stream.WriteAsync(frame, token); // throws IOException if connection died
// after
if (!host.IsConnectionOpen) { await host.RestartAsync(token); }
await stream.WriteAsync(frame, token);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!connection.IsOpen) { await ReconnectAsync(ct); } // before every write batch

Type guard

bool CanSend(FfiRuntimeHost h) => h is { IsDisposed: false } && h.IsConnectionOpen;

Try / catch

try { await sendStream.WriteAsync(frame, ct); }
catch (IOException ex) when (ex.Message.Contains("Failed to write a frame")) {
    logger.LogWarning(ex, "Runtime connection write failed; reopening");
    await ReconnectAsync(ct);
}

Prevention

When it happens

Trigger: Sending a request/notification while the native connection is closed or broken; the runtime returning failure from its write callback (peer shut down, host already stopped, internal buffer error).

Common situations: Writing after the runtime crashed or was shut down; racing a StartAsync failure with pending sends; native host exited mid-session (e.g. killed process, failed connection open on the native side).

Related errors


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

Appendix: source

Thrown at dotnet/src/FfiRuntimeHost.cs:630

        public override long Length => throw new NotSupportedException();
        public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
        public override void Flush() { }
        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
        public override void SetLength(long value) => throw new NotSupportedException();
        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
    }

    /// <summary>
    /// A write-only stream that forwards each frame to the native
    /// <c>connection_write</c> export.
    /// </summary>
    private sealed class CallbackSendStream(FrameWriter write) : Stream
    {
        private void WriteFrame(ReadOnlySpan<byte> frame)
        {
            if (!write(frame))
            {
                throw new IOException("Failed to write a frame to the in-process runtime connection.");
            }
        }

        public override void Write(byte[] buffer, int offset, int count) => WriteFrame(buffer.AsSpan(offset, count));

#if !NETSTANDARD2_0
        public override void Write(ReadOnlySpan<byte> buffer) => WriteFrame(buffer);

        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            WriteFrame(buffer.Span);
            return ValueTask.CompletedTask;
        }
#endif

        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            WriteFrame(buffer.AsSpan(offset, count));

View on GitHub (pinned to cd8cf15dc3)