Skip to content

KTOR-9606 Fix KotlinxSerializationConverter deserialization of empty channel#5652

Open
Aleksei Tirman (Stexxe) wants to merge 5 commits into
release/3.xfrom
stexxe/converter-delayed-close
Open

KTOR-9606 Fix KotlinxSerializationConverter deserialization of empty channel#5652
Aleksei Tirman (Stexxe) wants to merge 5 commits into
release/3.xfrom
stexxe/converter-delayed-close

Conversation

@Stexxe

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors Kotlinx deserialization to buffer remaining input and probe extensions from that buffer; adds JVM tests for Kotlinx, Gson, Jackson, and Jackson3 converters that exercise deserialization from an initially empty ByteChannel closed after a short delay.

Changes

Empty Channel Handling in Serialization Converters

Layer / File(s) Summary
Kotlinx deserialize: buffer and probe
ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt, ktor-shared/ktor-serialization/ktor-serialization-kotlinx/build.gradle.kts, ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt
Removed wildcard imports; deserialize now reads remaining content into a packet, probes KotlinxSerializationExtension with a ByteReadChannel over that packet, uses contentPacket.exhausted() to detect empty input, commonTest deps added, and tests assert null for empty delayed-close channels.
Gson converter empty-channel test
ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt
Adds Payload and a test that closes an initially empty ByteChannel after a short delay and asserts GsonConverter.deserialize returns null.
Jackson & Jackson3 converter empty-channel tests
ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt, ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt
Adds tests that close an empty ByteChannel after a delay and assert JacksonConverter.deserialize/Jackson3 variant throw JsonConvertException whose cause is MismatchedInputException.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • bjhham
  • e5l
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is incomplete, providing only a link to the issue without following the template structure requiring Subsystem, Motivation, and Solution sections. Add complete description sections including Subsystem, detailed Motivation explaining the bug, and Solution describing the fix approach.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references the issue (KTOR-9606) and accurately describes the main change: fixing KotlinxSerializationConverter's deserialization behavior for empty channels.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stexxe/converter-delayed-close

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt (1)

25-25: ⚡ Quick win

Use a backticked descriptive test name.

Please rename this test to backtick style (for example, fun `throws exception for empty channel`()) to align with test naming rules.

As per coding guidelines **/*Test.{kt,kts}: Prefer descriptive test names in backticks: describe what is being tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt`
at line 25, Rename the test function throwsExceptionForEmptyChannel to a
backticked descriptive name (e.g. `fun \`throws exception for empty
channel\`()`) to follow the project’s test naming convention; locate the
function definition in ConverterTest (the one currently declared as
throwsExceptionForEmptyChannel = runTest { ... }) and change its identifier to a
backticked, human-readable phrase while keeping the existing body and runTest
call unchanged.
ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt (1)

22-22: ⚡ Quick win

Use a backticked descriptive test name.

Please rename this test to backtick style (for example, fun `returns null for empty channel with delayed close`()) to align with test naming rules.

As per coding guidelines **/*Test.{kt,kts}: Prefer descriptive test names in backticks: describe what is being tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt`
at line 22, Rename the test function returnsNullForEmptyChannelWithDelayedClose
to use a backticked descriptive name (e.g. `returns null for empty channel with
delayed close`) in ConverterTest.kt so it follows the project's test naming
convention; update the function declaration for
returnsNullForEmptyChannelWithDelayedClose to the backticked form and ensure any
references (invocations or annotations) remain valid.
ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt (1)

24-24: ⚡ Quick win

Use a backticked descriptive test name.

Please rename this test to backtick style (for example, fun `returns null for empty channel with delayed close`()) to match the repository’s test naming convention.

As per coding guidelines **/*Test.{kt,kts}: Prefer descriptive test names in backticks: describe what is being tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt`
at line 24, Rename the test function returnsNullForEmptyChannelWithDelayedClose
to a backticked descriptive name following the repo convention, e.g. change the
function declaration to use a backticked identifier like `fun `returns null for
empty channel with delayed close`()`, updating any references to the function
name (if any) so the test runner executes it correctly; modify the declaration
in ConverterTest.kt where returnsNullForEmptyChannelWithDelayedClose is defined.
ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt (1)

25-25: ⚡ Quick win

Use a backticked descriptive test name.

Please rename this test to backtick style (for example, fun `throws exception for empty channel`()) to align with test naming rules.

As per coding guidelines **/*Test.{kt,kts}: Prefer descriptive test names in backticks: describe what is being tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt`
at line 25, Rename the test function throwsExceptionForEmptyChannel to a
descriptive backticked name (e.g. fun `throws exception for empty channel`() =
runTest { ... }) to follow the test-naming guideline; update the function
declaration in ConverterTest (keep the existing runTest block and body
unchanged) and adjust any internal references or imports if the symbol is
referenced elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt`:
- Line 22: Rename the test function returnsNullForEmptyChannelWithDelayedClose
to use a backticked descriptive name (e.g. `returns null for empty channel with
delayed close`) in ConverterTest.kt so it follows the project's test naming
convention; update the function declaration for
returnsNullForEmptyChannelWithDelayedClose to the backticked form and ensure any
references (invocations or annotations) remain valid.

In
`@ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt`:
- Line 25: Rename the test function throwsExceptionForEmptyChannel to a
descriptive backticked name (e.g. fun `throws exception for empty channel`() =
runTest { ... }) to follow the test-naming guideline; update the function
declaration in ConverterTest (keep the existing runTest block and body
unchanged) and adjust any internal references or imports if the symbol is
referenced elsewhere.

In
`@ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt`:
- Line 25: Rename the test function throwsExceptionForEmptyChannel to a
backticked descriptive name (e.g. `fun \`throws exception for empty
channel\`()`) to follow the project’s test naming convention; locate the
function definition in ConverterTest (the one currently declared as
throwsExceptionForEmptyChannel = runTest { ... }) and change its identifier to a
backticked, human-readable phrase while keeping the existing body and runTest
call unchanged.

In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt`:
- Line 24: Rename the test function returnsNullForEmptyChannelWithDelayedClose
to a backticked descriptive name following the repo convention, e.g. change the
function declaration to use a backticked identifier like `fun `returns null for
empty channel with delayed close`()`, updating any references to the function
name (if any) so the test runner executes it correctly; modify the declaration
in ConverterTest.kt where returnsNullForEmptyChannelWithDelayedClose is defined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e0eba00-f63d-465e-bf7a-657fb16371a7

📥 Commits

Reviewing files that changed from the base of the PR and between 0dbd131 and e447594.

📒 Files selected for processing (6)
  • ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/build.gradle.kts
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt`:
- Around line 60-63: The empty-body fast path is currently gated by
extensions.isNotEmpty(), making it format-dependent; update the logic in
KotlinxSerializationConverter (the block computing fromExtension using
extensions.asFlow().map { it.deserialize(charset, typeInfo,
ByteReadChannel(contentPacket)) }.firstOrNull { ... }) so that
contentPacket.exhausted() is checked independently of extensions.isNotEmpty():
first attempt to short-circuit and return when contentPacket.exhausted()
(format-agnostic), otherwise evaluate fromExtension and only then return it when
extensions.isNotEmpty() and fromExtension != null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 67045ed0-ffcc-4c85-a1b5-76d04492abba

📥 Commits

Reviewing files that changed from the base of the PR and between e447594 and bdc6d0d.

📒 Files selected for processing (1)
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt

@osipxd Osip Fatkullin (osipxd) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Offtop:
It feels weird that we have three ways receive payload:

  1. call.receive<Payload>()
  2. call.receiveNullable<Payload>()
  3. call.receiveNullable<Payload?>()

But this is out of the scope of this PR.

@osipxd

Copy link
Copy Markdown
Member

It seems that my comment was actually wrong. This commit eb14d53 has added extensions.isNotEmpty() intentionally, so last commit should be probably dropped

@Stexxe Aleksei Tirman (Stexxe) force-pushed the stexxe/converter-delayed-close branch from 85ddd9e to 7240236 Compare May 29, 2026 06:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt (2)

24-24: ⚡ Quick win

Prefer backtick test names for readability and consistency.

Rename this test to a descriptive backtick form.

Suggested diff
-    fun returnsNullForEmptyChannelWithDelayedClose() = runTest {
+    fun `returns null for empty channel with delayed close`() = runTest {

As per coding guidelines: "Prefer descriptive test names in backticks: describe what is being tested".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt`
at line 24, The test function returnsNullForEmptyChannelWithDelayedClose should
be renamed to a descriptive backtick-style name for readability and consistency;
locate the function returnsNullForEmptyChannelWithDelayedClose in
ConverterTest.kt and change its declaration to use a backtick name like `returns
null for empty channel with delayed close` (i.e., fun `returns null for empty
channel with delayed close`() = runTest { ... }) ensuring any internal
references or test frameworks pick up the new name.

7-10: ⚡ Quick win

Use io.ktor.* star imports to match repository import policy.

These imports should follow the configured star-import rule for Ktor packages.

Suggested diff
-import io.ktor.serialization.kotlinx.json.DefaultJson
-import io.ktor.util.reflect.typeInfo
-import io.ktor.utils.io.ByteChannel
-import io.ktor.utils.io.charsets.Charsets
+import io.ktor.serialization.kotlinx.json.*
+import io.ktor.util.reflect.*
+import io.ktor.utils.io.*
+import io.ktor.utils.io.charsets.*

As per coding guidelines: "Use star imports for io.ktor.* (as configured in .editorconfig)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt`
around lines 7 - 10, Replace the explicit Ktor imports in ConverterTest.kt
(e.g., io.ktor.serialization.kotlinx.json.DefaultJson,
io.ktor.util.reflect.typeInfo, io.ktor.utils.io.ByteChannel,
io.ktor.utils.io.charsets.Charsets) with the repository's configured star-style
import for Ktor packages (use a single import like io.ktor.* or the appropriate
io.ktor.* grouping) so the file follows the .editorconfig rule; ensure no other
non-Ktor symbols are collapsed and run the project linter to confirm the import
style passes.
ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt (2)

22-22: ⚡ Quick win

Use a descriptive backtick test name.

Please rename this test to the preferred backtick style.

Suggested diff
-    fun returnsNullForEmptyChannelWithDelayedClose() = runTest {
+    fun `returns null for empty channel with delayed close`() = runTest {

As per coding guidelines: "Prefer descriptive test names in backticks: describe what is being tested".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt`
at line 22, Rename the test function returnsNullForEmptyChannelWithDelayedClose
to a descriptive backtick-style name by changing its declaration to use a
backticked string (e.g. fun `returns null for empty channel with delayed
close`() = runTest { ... }) so the test reads like a sentence; update the
function name in ConverterTest.kt wherever referenced and keep the existing body
and runTest usage unchanged.

5-8: ⚡ Quick win

Switch io.ktor.* imports to star imports per project rules.

The current explicit Ktor imports conflict with repository import configuration.

Suggested diff
-import io.ktor.serialization.gson.GsonConverter
-import io.ktor.util.reflect.typeInfo
-import io.ktor.utils.io.ByteChannel
-import io.ktor.utils.io.charsets.Charsets
+import io.ktor.serialization.gson.*
+import io.ktor.util.reflect.*
+import io.ktor.utils.io.*
+import io.ktor.utils.io.charsets.*

As per coding guidelines: "Use star imports for io.ktor.* (as configured in .editorconfig)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt`
around lines 5 - 8, The imports from io.ktor.* (GsonConverter, typeInfo,
ByteChannel, Charsets) should use star imports to match the repo's .editorconfig
rule; replace the explicit io.ktor.serialization.gson.GsonConverter,
io.ktor.util.reflect.typeInfo, io.ktor.utils.io.ByteChannel and
io.ktor.utils.io.charsets.Charsets imports with the appropriate io.ktor.*
wildcard imports so the file follows the project's import convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt`:
- Line 22: Rename the test function returnsNullForEmptyChannelWithDelayedClose
to a descriptive backtick-style name by changing its declaration to use a
backticked string (e.g. fun `returns null for empty channel with delayed
close`() = runTest { ... }) so the test reads like a sentence; update the
function name in ConverterTest.kt wherever referenced and keep the existing body
and runTest usage unchanged.
- Around line 5-8: The imports from io.ktor.* (GsonConverter, typeInfo,
ByteChannel, Charsets) should use star imports to match the repo's .editorconfig
rule; replace the explicit io.ktor.serialization.gson.GsonConverter,
io.ktor.util.reflect.typeInfo, io.ktor.utils.io.ByteChannel and
io.ktor.utils.io.charsets.Charsets imports with the appropriate io.ktor.*
wildcard imports so the file follows the project's import convention.

In
`@ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt`:
- Line 24: The test function returnsNullForEmptyChannelWithDelayedClose should
be renamed to a descriptive backtick-style name for readability and consistency;
locate the function returnsNullForEmptyChannelWithDelayedClose in
ConverterTest.kt and change its declaration to use a backtick name like `returns
null for empty channel with delayed close` (i.e., fun `returns null for empty
channel with delayed close`() = runTest { ... }) ensuring any internal
references or test frameworks pick up the new name.
- Around line 7-10: Replace the explicit Ktor imports in ConverterTest.kt (e.g.,
io.ktor.serialization.kotlinx.json.DefaultJson, io.ktor.util.reflect.typeInfo,
io.ktor.utils.io.ByteChannel, io.ktor.utils.io.charsets.Charsets) with the
repository's configured star-style import for Ktor packages (use a single import
like io.ktor.* or the appropriate io.ktor.* grouping) so the file follows the
.editorconfig rule; ensure no other non-Ktor symbols are collapsed and run the
project linter to confirm the import style passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd7de9b6-b0db-4208-a822-052bd67897aa

📥 Commits

Reviewing files that changed from the base of the PR and between 85ddd9e and 7240236.

📒 Files selected for processing (6)
  • ktor-shared/ktor-serialization/ktor-serialization-gson/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/build.gradle.kts
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/test/io/ktor/serialization/kotlinx/ConverterTest.kt
🚧 Files skipped from review as they are similar to previous changes (4)
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/build.gradle.kts
  • ktor-shared/ktor-serialization/ktor-serialization-jackson3/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-jackson/jvm/test/ConverterTest.kt
  • ktor-shared/ktor-serialization/ktor-serialization-kotlinx/common/src/io/ktor/serialization/kotlinx/KotlinxSerializationConverter.kt

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.

3 participants