Skip to content

fix!(server): #575 Rethrow CancellationException in StdioServerTransport, fix ReadBuffer - #571

Merged
kpavlov merged 7 commits into
mainfrom
kpavlov/564-stdio-server-ce
Mar 3, 2026
Merged

fix!(server): #575 Rethrow CancellationException in StdioServerTransport, fix ReadBuffer#571
kpavlov merged 7 commits into
mainfrom
kpavlov/564-stdio-server-ce

Conversation

@kpavlov

@kpavlov kpavlov commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Rethrow CancellationException in StdioServerTransport, fix ReadBuffer

Changes

  1. StdioServerTransport

    • Replace generic Throwable catches with specific CancellationException and Exception for clarity and correctness. breaking change Throwable is not handled any more!
    • Refactor launching jobs to separate methods
    • Introduce READ_BUFFER_SIZE constant to replace inline buffer size literals.
    • Add suppress annotations for clearer intent and constructor documentation for better usability.
    • Extend StdioServerTransportTest
  2. ReadBuffer

    Previously, readMessage() returned null after consuming an unparseable line even when more complete lines existed in the buffer. This caused valid messages following a bad line in the same chunk to be silently dropped until the next chunk arrived (or forever, in tests).

    Fix the loop so null means only "no complete line available", not "encountered a parse failure". Blank/whitespace-only lines are now silently skipped via isBlank() rather than forwarded to the deserializer and logged as errors.

    Refactor the method into three focused helpers:

    • readMessage() — outer loop over lines
    • readLine() — consume the next newline-delimited line from the buffer
    • tryRecover() — attempt deserialization from the first '{' onward
  3. Add utility method runIntegrationTest for integration testing (runBlocking + withTimeout). Use it in StdioServerTransportTest

  4. Store processing Job reference and calcelAndJoin() it on close. Partially addressing StdioServerTransport: The coroutine scope that's used in this transport is completely detached from the coroutine tree #574

Motivation and Context

See #575, #564, #242

How Has This Been Tested?

Unit test added

Breaking Changes

Semantic change: Throwable and CancellationException are not handled any more!

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@kpavlov kpavlov changed the title fix!(server): Rethrow CancellationException, add buffer size constant in StdioServerTransport fix!(server): #564 Rethrow CancellationException, add buffer size constant in StdioServerTransport Mar 2, 2026
@kpavlov kpavlov added the bugfix label Mar 2, 2026
@kpavlov kpavlov changed the title fix!(server): #564 Rethrow CancellationException, add buffer size constant in StdioServerTransport fix!(server): #575 Rethrow CancellationException, add buffer size constant in StdioServerTransport Mar 2, 2026
@kpavlov
kpavlov force-pushed the kpavlov/564-stdio-server-ce branch from 7c2669c to 91b2d9a Compare March 2, 2026 09:33
@kpavlov kpavlov changed the title fix!(server): #575 Rethrow CancellationException, add buffer size constant in StdioServerTransport fix!(server): #575 Rethrow CancellationException in StdioServerTransport, fix ReadBuffer Mar 2, 2026
@kpavlov
kpavlov force-pushed the kpavlov/564-stdio-server-ce branch 2 times, most recently from 18efbcd to 9e2cf25 Compare March 2, 2026 10:02
@kpavlov
kpavlov marked this pull request as ready for review March 2, 2026 10:02
@kpavlov
kpavlov requested review from devcrocod and e5l March 2, 2026 10:02
@kpavlov
kpavlov requested a review from Amaneusz March 2, 2026 11:37
devcrocod
devcrocod previously approved these changes Mar 2, 2026

@devcrocod devcrocod 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.

lgtm

I’ve left a few comments, but they’re minor and mostly related to code style

@kpavlov
kpavlov force-pushed the kpavlov/564-stdio-server-ce branch 2 times, most recently from b16cbab to aef2c76 Compare March 2, 2026 16:01
@kpavlov
kpavlov requested a review from devcrocod March 2, 2026 16:11
@codecov-commenter

codecov-commenter commented Mar 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.22034% with 4 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...protocol/kotlin/sdk/server/StdioServerTransport.kt 87.09% 1 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@kpavlov

kpavlov commented Mar 2, 2026

Copy link
Copy Markdown
Contributor Author

Thank you, @devcrocod, for your comments! I tried to address most of them, please have a look

@kpavlov
kpavlov marked this pull request as draft March 2, 2026 20:16
@kpavlov
kpavlov force-pushed the kpavlov/564-stdio-server-ce branch 3 times, most recently from 1d05801 to 61f8392 Compare March 2, 2026 21:41

readChannel.close()

processingJob?.cancelAndJoin()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kpavlov
kpavlov marked this pull request as ready for review March 2, 2026 22:10
e5l
e5l previously approved these changes Mar 3, 2026

@e5l e5l 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.

please check the comments before merging

@Suppress("TooGenericExceptionCaught")
val message = try {
deserializeMessage(line)
} catch (e: Exception) {

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.

Suggested change
} catch (e: Exception) {
} catch (e: Throwable) {

} catch (e: Throwable) {
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {

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.

Suggested change
} catch (e: Exception) {
} catch (e: Throwable) {

val message = try {
readBuffer.readMessage()
} catch (e: Throwable) {
} catch (e: Exception) {

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.

Could you tell me why we want to skip logging Error? https://docs.oracle.com/javase/8/docs/api/java/lang/Error.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Things like OutOfMemoryError are not actionable and usually not handled by the application and propagated.

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.

they still needs to be logged by the configured logger

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Logging is never guaranteed in the situation when JVM is crashing.
E.g., an OutOfMemoryError usually means the JVM has insufficient memory to allocate additional objects. Application loggers often allocate new objects (e.g., strings, log event objects, buffers) or interact with subsystems (e.g., disk I/O, network I/O). These operations may fail or behave unpredictably under memory pressure.

Logging to System.err might make sense, but is still questionable.

kpavlov added 4 commits March 3, 2026 09:11
…ServerTransport

- Replace generic `Throwable` catches with specific `CancellationException` and `Exception` for clarity and correctness. **breaking change** `Throwable` is not handled any more!
- Refactor launching jobs to separate methods
- Introduce `READ_BUFFER_SIZE` constant to replace inline buffer size literals.
- Add suppress annotations for clearer intent and constructor documentation for better usability.
- Extend StdioServerTransportTest
Previously, readMessage() returned null after consuming an unparseable line even when more complete lines existed in the buffer. This caused valid messages following a bad line in the same chunk to be silently dropped until the next chunk arrived (or forever, in tests).

Fix the loop so null means only "no complete line available", not "encountered a parse failure". Blank/whitespace-only lines are now silently skipped via isBlank() rather than forwarded to the deserializer and logged as errors.

Refactor the method into three focused helpers:
 - readMessage() — outer loop over lines
 - readLine()    — consume the next newline-delimited line from the buffer
 - tryRecover()  — attempt deserialization from the first '{' onward
- Add test verifying server continues processing messages despite handler exceptions.
- Introduce `ParameterizedTest` for various exception types in message handler.
- Ensure `onError` is not invoked for `CancellationException`.
kpavlov added 2 commits March 3, 2026 09:11
…dioServerTransport tests

- Introduced `runIntegrationTest` utility for consistent test execution with timeout configuration.
- Adjusted test utils module in Gradle build to include new helper.
- Simplified exception handling and concurrency in StdioServerTransportTest.
…verTransport

- Refactored job management to include explicit tracking for the processing job. Enhanced resource cleanup logic.
- Updated constructor parameters KDoc to source/sink to match actual types. Renamed internal variable `outputWriter` to `outputSink` to reflect updated abstraction.
- Added helper method `logJobCompletion` for consistent logging of job lifecycle events.
@kpavlov
kpavlov force-pushed the kpavlov/564-stdio-server-ce branch from 61f8392 to 9d47a71 Compare March 3, 2026 07:11
…t and tests

Addressed PR comments:

- Updated exception handling in `StdioServerTransport` to catch `Throwable` instead of `Exception`.
- Adjusted test methods and helper functions to handle `Throwable`.
@kpavlov

kpavlov commented Mar 3, 2026

Copy link
Copy Markdown
Contributor Author

@e5l, I’ve reverted to using Throwables. Could you take a look?

@kpavlov
kpavlov merged commit a388180 into main Mar 3, 2026
13 checks passed
@kpavlov
kpavlov deleted the kpavlov/564-stdio-server-ce branch March 3, 2026 14:10
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.

4 participants