Skip to content

Commit 241448d

Browse files
committed
feat: add AUTOCLOSEABLE_TIMEOUT_SECONDS constant and enhance documentation for graceful shutdown and event handling troubleshooting
1 parent df0832c commit 241448d

3 files changed

Lines changed: 54 additions & 5 deletions

File tree

src/main/java/com/github/copilot/sdk/CopilotClient.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public final class CopilotClient implements AutoCloseable {
6161

6262
private static final Logger LOG = Logger.getLogger(CopilotClient.class.getName());
6363

64+
/**
65+
* Timeout, in seconds, used by {@link #close()} when waiting for graceful
66+
* shutdown via {@link #stop()}.
67+
*/
68+
public static final int AUTOCLOSEABLE_TIMEOUT_SECONDS = 10;
6469
private final CopilotClientOptions options;
6570
private final CliServerManager serverManager;
6671
private final LifecycleEventManager lifecycleManager = new LifecycleEventManager();
@@ -221,6 +226,7 @@ public CompletableFuture<Void> stop() {
221226
* @return A future that completes when the client is stopped
222227
*/
223228
public CompletableFuture<Void> forceStop() {
229+
disposed = true;
224230
sessions.clear();
225231
return cleanupConnection();
226232
}
@@ -554,13 +560,27 @@ private CompletableFuture<Connection> ensureConnected() {
554560
return connectionFuture;
555561
}
556562

563+
/**
564+
* Closes this client using graceful shutdown semantics.
565+
* <p>
566+
* This method is intended for {@code try-with-resources} usage and blocks while
567+
* waiting for {@link #stop()} to complete, up to
568+
* {@link #AUTOCLOSEABLE_TIMEOUT_SECONDS} seconds. If shutdown fails or times
569+
* out, the error is logged at {@link Level#FINE} and the method returns.
570+
* <p>
571+
* This method is idempotent.
572+
*
573+
* @see #stop()
574+
* @see #forceStop()
575+
* @see #AUTOCLOSEABLE_TIMEOUT_SECONDS
576+
*/
557577
@Override
558578
public void close() {
559579
if (disposed)
560580
return;
561581
disposed = true;
562582
try {
563-
forceStop().get(5, TimeUnit.SECONDS);
583+
stop().get(AUTOCLOSEABLE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
564584
} catch (Exception e) {
565585
LOG.log(Level.FINE, "Error during close", e);
566586
}

src/site/markdown/advanced.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,8 @@ Use `forceStop()` when you need to terminate immediately, such as during error r
495495
client.forceStop().get();
496496
```
497497

498-
> **Tip:** In `try-with-resources` blocks, `close()` delegates to `stop()`, so graceful cleanup happens automatically.
498+
> **Tip:** In `try-with-resources` blocks, `close()` delegates to `stop()`, so graceful session cleanup happens automatically.
499+
> `close()` is blocking and waits up to `CopilotClient.AUTOCLOSEABLE_TIMEOUT_SECONDS` seconds for shutdown to complete.
499500
500501
---
501502

src/site/markdown/documentation.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This guide covers common use cases for the Copilot SDK for Java. For complete AP
88

99
- [Basic Usage](#Basic_Usage)
1010
- [Handling Responses](#Handling_Responses)
11+
- [Troubleshooting Event Handling](#Troubleshooting_Event_Handling)
1112
- [Event Types Reference](#Event_Types_Reference)
1213
- [Streaming Responses](#Streaming_Responses)
1314
- [Session Operations](#Session_Operations)
@@ -64,11 +65,38 @@ System.out.println(response.getData().getContent());
6465
For more control, subscribe to events and use `send()`:
6566

6667
> **Exception isolation:** If a handler throws an exception, the SDK logs the
67-
> error and continues dispatching to remaining handlers. One misbehaving handler
68-
> will never prevent others from executing. You can customize error handling with
69-
> `session.setEventErrorHandler()` — see the
68+
> error. By default, dispatch stops after the first handler error
69+
> (`PROPAGATE_AND_LOG_ERRORS`). To continue dispatching to remaining handlers,
70+
> set `EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS`. You can customize error
71+
> handling with `session.setEventErrorHandler()` — see the
7072
> [Advanced Usage](advanced.html#Custom_Event_Error_Handler) guide.
7173
74+
## Troubleshooting Event Handling
75+
76+
### Symptoms of policy misconfiguration
77+
78+
- You registered multiple `session.on(...)` handlers, but only the first one runs
79+
- A handler throws once and later handlers stop receiving events
80+
- You expected "best effort" fan-out, but dispatch halts on errors
81+
82+
### Fix
83+
84+
Set the event error policy to suppress-and-continue:
85+
86+
```java
87+
session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);
88+
```
89+
90+
Optionally add a custom error handler for observability:
91+
92+
```java
93+
session.setEventErrorHandler((event, exception) -> {
94+
System.err.println("Handler failed for event " + event.getType() + ": " + exception.getMessage());
95+
});
96+
```
97+
98+
Use `PROPAGATE_AND_LOG_ERRORS` when you want fail-fast behavior.
99+
72100
```java
73101
var done = new CompletableFuture<Void>();
74102

0 commit comments

Comments
 (0)