Skip to content

Add missing usage_update type and forward-compatible SessionUpdate deserialization#68

Merged
nerzhulart merged 7 commits intomasterfrom
copilot/fix-deserialization-usage-update
Feb 23, 2026
Merged

Add missing usage_update type and forward-compatible SessionUpdate deserialization#68
nerzhulart merged 7 commits intomasterfrom
copilot/fix-deserialization-usage-update

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 22, 2026

SDK crashed on usage_update notifications. Type exists in ACP unstable schema v0.9.1 but wasn't implemented. Also adds forward compatibility for unknown SessionUpdate types.

Changes

Missing schema types:

  • SessionUpdate.UsageUpdate - tracks token usage (used: Long, size: Long, cost: Cost?)
  • Cost - cost representation (amount: Double, currency: String)

Forward compatibility:

  • SessionUpdate.UnknownSessionUpdate - captures unknown types with full JSON preservation
  • Custom SessionUpdateSerializer - explicitly handles 11 known types, falls back to UnknownSessionUpdate

Supporting changes:

  • Updated API dump for binary compatibility tracking
  • Fixed exhaustive when in sample code
  • Version bump to 0.15.4

Example

// Before: crash
val json = """{"sessionUpdate":"usage_update","used":0,"size":200000,"cost":{"amount":0,"currency":"USD"}}"""
ACPJson.decodeFromString(SessionUpdate.serializer(), json)  // ❌ JsonDecodingException

// After: graceful handling
val update = ACPJson.decodeFromString(SessionUpdate.serializer(), json)
when (update) {
    is SessionUpdate.UsageUpdate -> handleUsage(update)
    is SessionUpdate.UnknownSessionUpdate -> log("Unknown: ${update.sessionUpdateType}")
    // ... other types
}

Pattern mirrors existing AuthMethod.UnknownAuthMethod approach.

Original prompt

This section details on the original issue you should resolve

<issue_title>Deserialization fails on usage_update</issue_title>
<issue_description>I have following error working with opencode:
2026-02-20 11:35:40,794 [7698226] INFO - #c.i.m.l.a.a.c.s.AcpModelManager - Agent acp.registry.opencode initial model: Amazon Bedrock/Claude Sonnet 4.6 (US) (high) (from saved settings)

2026-02-20 11:35:40,794 [7698226] INFO - #c.i.m.l.a.a.c.AcpSessionLifecycleManager - [OpenCode] Reusing existing session for chat cd9e1168-6855-4b54-8020-4c2b29d64192

2026-02-20 11:35:41,036 [7698468] SEVERE - com.agentclientprotocol.protocol.Protocol - Error handling notification MethodName(name=session/update)

kotlinx.serialization.json.internal.JsonDecodingException: Serializer for subclass 'usage_update' is not found in the polymorphic scope of 'SessionUpdate'.

Check if class with serial name 'usage_update' exists and serializer is registered in a corresponding SerializersModule.

To be registered automatically, class 'usage_update' has to be '@serializable', and the base class 'SessionUpdate' has to be sealed and '@serializable'.

JSON input: {"sessionUpdate":"usage_update","used":0,"size":200000,"cost":{"amount":0,"currency":"USD"}}

at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)

at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)

at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:370)

at kotlinx.serialization.internal.TaggedDecoder.decodeSerializableValue(Tagged.kt:207)

at kotlinx.serialization.internal.TaggedDecoder.decodeSerializableElement$lambda$1(Tagged.kt:279)

at kotlinx.serialization.internal.TaggedDecoder.tagBlock(Tagged.kt:294)

at kotlinx.serialization.internal.TaggedDecoder.decodeSerializableElement(Tagged.kt:279)

at com.agentclientprotocol.model.SessionNotification$$serializer.deserialize(Requests.kt:805)

at com.agentclientprotocol.model.SessionNotification$$serializer.deserialize(Requests.kt:805)

at kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:351)

at kotlinx.serialization.json.internal.TreeJsonDecoderKt.readJson(TreeJsonDecoder.kt:25)

at kotlinx.serialization.json.Json.decodeFromJsonElement(Json.kt:170)

at com.agentclientprotocol.protocol.Protocol_extensionsKt$setNotificationHandler$1.invokeSuspend(Protocol.extensions.kt:101)

at com.agentclientprotocol.protocol.Protocol_extensionsKt$setNotificationHandler$1.invoke(Protocol.extensions.kt)

at com.agentclientprotocol.protocol.Protocol_extensionsKt$setNotificationHandler$1.invoke(Protocol.extensions.kt)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1$1.invokeSuspend(Protocol.kt:293)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1$1.invoke(Protocol.kt)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1$1.invoke(Protocol.kt)

at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:44)

at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:157)

at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1.invokeSuspend(Protocol.kt:292)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1.invoke(Protocol.kt)

at com.agentclientprotocol.protocol.Protocol$setNotificationHandlerRaw$1$1.invoke(Protocol.kt)

at com.agentclientprotocol.protocol.Protocol.handleNotification(Protocol.kt:441)

at com.agentclientprotocol.protocol.Protocol.handleIncomingMessage(Protocol.kt:353)

at com.agentclientprotocol.protocol.Protocol.access$handleIncomingMessage(Protocol.kt:122)

at com.agentclientprotocol.protocol.Protocol$start$2.invokeSuspend(Protocol.kt:172)

at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)

at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)

at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:610)

at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runDefaultDispatcherTask(CoroutineScheduler.kt:1194)

at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:906)

at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:775)

at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:762)

2026-02-20 11:35:41,037 [7698469] INFO - #c.i.m.l.c.n.ChatNotificationService - Send notification for agent ChatSessionAgentId acp.registry.opencode: null -> TaskFinished

2026-02-20 11:35:42,474 [7699906] INFO - #c.i.w.i.i.j.s.JpsGlobalModelSynchronizerImpl - Saving global entities com.intellij.platform.workspace.jps.entities.SdkEntity to files

2026-02-20 11:35:42,480 [7699912] INFO - #c.i.w.i.i.j.s.JpsGlobalModelSynchronize...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@nerzhulart nerzhulart marked this pull request as ready for review February 22, 2026 14:50
Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix deserialization issue for usage_update Add missing UsageUpdate SessionUpdate type Feb 22, 2026
Copilot AI requested a review from nerzhulart February 22, 2026 14:53
Copilot AI changed the title Add missing UsageUpdate SessionUpdate type Add missing UsageUpdate SessionUpdate type from ACP unstable schema Feb 22, 2026
…on update types

Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copilot AI changed the title Add missing UsageUpdate SessionUpdate type from ACP unstable schema Add forward compatibility for SessionUpdate deserialization Feb 22, 2026
Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copilot AI changed the title Add forward compatibility for SessionUpdate deserialization Add SessionUpdate forward compatibility and missing usage_update type Feb 22, 2026
Copilot AI requested a review from nerzhulart February 22, 2026 16:45
…ypes

Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copilot AI changed the title Add SessionUpdate forward compatibility and missing usage_update type Add missing SessionUpdate types and forward compatibility Feb 22, 2026
Copilot AI requested a review from nerzhulart February 22, 2026 17:27
nerzhulart

This comment was marked as outdated.

@nerzhulart nerzhulart self-requested a review February 22, 2026 17:50
Copy link
Copy Markdown
Contributor

@nerzhulart nerzhulart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nerzhulart
Copy link
Copy Markdown
Contributor

@copilot отдуши братан

@nerzhulart
Copy link
Copy Markdown
Contributor

@copilot promote minor version

Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 23, 2026

@copilot promote minor version

Version promoted from 0.15.3 to 0.16.0 in commit 4250273.

Co-authored-by: nerzhulart <623624+nerzhulart@users.noreply.github.com>
Copilot AI changed the title Add missing SessionUpdate types and forward compatibility Add missing usage_update type and forward-compatible SessionUpdate deserialization Feb 23, 2026
@nerzhulart nerzhulart merged commit ed32572 into master Feb 23, 2026
2 of 3 checks passed
@nerzhulart nerzhulart deleted the copilot/fix-deserialization-usage-update branch February 23, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deserialization fails on usage_update

3 participants