Skip to content

Commit 4f97776

Browse files
authored
Better cancellation token error suppression (#268)
1 parent bc7c508 commit 4f97776

File tree

5 files changed

+39
-20
lines changed

5 files changed

+39
-20
lines changed

src/handlers/DocumentHandler.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { LintTrigger } from '../services/cfnLint/CfnLintService';
1212
import { ValidationTrigger } from '../services/guard/GuardService';
1313
import { publishValidationDiagnostics } from '../stacks/actions/StackActionOperations';
1414
import { LoggerFactory } from '../telemetry/LoggerFactory';
15+
import { CancellationError } from '../utils/Delayer';
1516

1617
const log = LoggerFactory.getLogger('DocumentHandler');
1718

@@ -36,8 +37,8 @@ export function didOpenHandler(components: ServerComponents): (event: TextDocume
3637

3738
components.cfnLintService.lintDelayed(content, uri, LintTrigger.OnOpen).catch((reason) => {
3839
// Handle cancellation gracefully - user might have closed/changed the document
39-
if (reason instanceof Error && reason.message.includes('Request cancelled')) {
40-
// Do nothing
40+
if (reason instanceof CancellationError) {
41+
// Do nothing - cancellation is expected behavior
4142
} else {
4243
log.error(reason, `Linting error for ${uri}`);
4344
}
@@ -105,8 +106,8 @@ export function didChangeHandler(
105106
// Trigger cfn-lint validation
106107
components.cfnLintService.lintDelayed(content, documentUri, LintTrigger.OnChange, true).catch((reason) => {
107108
// Handle both getTextDocument and linting errors
108-
if (reason instanceof Error && reason.message.includes('Request cancelled')) {
109-
// Do nothing
109+
if (reason instanceof CancellationError) {
110+
// Do nothing - cancellation is expected behavior
110111
} else {
111112
log.error(reason, `Error in didChange processing for ${documentUri}`);
112113
}
@@ -172,8 +173,8 @@ export function didSaveHandler(components: ServerComponents): (event: TextDocume
172173

173174
// Trigger cfn-lint validation
174175
components.cfnLintService.lintDelayed(documentContent, documentUri, LintTrigger.OnSave).catch((reason) => {
175-
if (reason instanceof Error && reason.message.includes('Request cancelled')) {
176-
// Do nothing
176+
if (reason instanceof CancellationError) {
177+
// Do nothing - cancellation is expected behavior
177178
} else {
178179
log.error(reason, `Linting error for ${documentUri}`);
179180
}

src/services/DiagnosticCoordinator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { LspDiagnostics } from '../protocol/LspDiagnostics';
66
import { ValidationManager } from '../stacks/actions/ValidationManager';
77
import { CFN_VALIDATION_SOURCE } from '../stacks/actions/ValidationWorkflow';
88
import { LoggerFactory } from '../telemetry/LoggerFactory';
9-
import { Delayer } from '../utils/Delayer';
9+
import { CancellationError, Delayer } from '../utils/Delayer';
1010

1111
type SourceToDiagnostics = Map<string, Diagnostic[]>;
1212

@@ -54,6 +54,10 @@ export class DiagnosticCoordinator {
5454
// Debounce the actual LSP publishing to avoid spam on keystrokes
5555
await this.delayer.delay(uri, () => this.publishToLsp(uri));
5656
} catch (error) {
57+
// Suppress cancellation errors as they are expected behavior
58+
if (error instanceof CancellationError) {
59+
return;
60+
}
5761
this.log.error(error, `Failed to publish diagnostics for source ${source}, URI ${uri}`);
5862
throw error;
5963
}

src/services/cfnLint/CfnLintService.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { LoggerFactory } from '../../telemetry/LoggerFactory';
1212
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
1313
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
1414
import { Closeable } from '../../utils/Closeable';
15-
import { Delayer } from '../../utils/Delayer';
15+
import { CancellationError, Delayer } from '../../utils/Delayer';
1616
import { extractErrorMessage } from '../../utils/Errors';
1717
import { byteSize } from '../../utils/String';
1818
import { DiagnosticCoordinator } from '../DiagnosticCoordinator';
@@ -632,12 +632,20 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
632632
}
633633

634634
// Service is ready, process based on trigger type
635-
if (trigger === LintTrigger.OnSave) {
636-
// For save operations: execute immediately (0ms delay)
637-
await this.delayer.delay(uri, () => this.lint(content, uri, forceUseContent), 0);
638-
} else {
639-
// For other triggers: use normal delayed execution
640-
await this.delayer.delay(uri, () => this.lint(content, uri, forceUseContent));
635+
try {
636+
if (trigger === LintTrigger.OnSave) {
637+
// For save operations: execute immediately (0ms delay)
638+
await this.delayer.delay(uri, () => this.lint(content, uri, forceUseContent), 0);
639+
} else {
640+
// For other triggers: use normal delayed execution
641+
await this.delayer.delay(uri, () => this.lint(content, uri, forceUseContent));
642+
}
643+
} catch (error) {
644+
// Suppress cancellation errors as they are expected behavior
645+
if (error instanceof CancellationError) {
646+
return;
647+
}
648+
throw error;
641649
}
642650
}
643651

src/services/guard/GuardService.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { LoggerFactory } from '../../telemetry/LoggerFactory';
1010
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
1111
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
1212
import { Closeable } from '../../utils/Closeable';
13-
import { Delayer } from '../../utils/Delayer';
13+
import { CancellationError, Delayer } from '../../utils/Delayer';
1414
import { extractErrorMessage } from '../../utils/Errors';
1515
import { readFileIfExistsAsync } from '../../utils/File';
1616
import { byteSize } from '../../utils/String';
@@ -377,9 +377,8 @@ export class GuardService implements SettingsConfigurable, Closeable {
377377
await this.delayer.delay(uri, () => this.validate(content, uri, forceUseContent));
378378
}
379379
} catch (error) {
380-
const errorMessage = extractErrorMessage(error);
381-
// Check if this is a cancellation error - these are normal during rapid typing
382-
if (errorMessage.includes('Request cancelled') || errorMessage.includes('cancelled')) {
380+
// Suppress cancellation errors as they are expected behavior
381+
if (error instanceof CancellationError) {
383382
return;
384383
}
385384
// For other errors, re-throw to be handled by caller

src/utils/Delayer.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
export class CancellationError extends Error {
2+
constructor(key: string) {
3+
super(`Request cancelled for key: ${key}`);
4+
this.name = 'CancellationError';
5+
}
6+
}
7+
18
interface DelayedRequest<T> {
29
executor: () => Promise<T>;
310
resolve: (result: T) => void;
@@ -142,7 +149,7 @@ export class Delayer<T> {
142149

143150
if (request) {
144151
this.pendingRequests.delete(key);
145-
request.reject(new Error(`Request cancelled for key: ${key}`));
152+
request.reject(new CancellationError(key));
146153
}
147154

148155
// Note: We don't cancel running requests as they can't be safely cancelled
@@ -163,7 +170,7 @@ export class Delayer<T> {
163170

164171
// Reject all pending requests
165172
for (const [key, request] of this.pendingRequests.entries()) {
166-
request.reject(new Error(`Request cancelled for key: ${key}`));
173+
request.reject(new CancellationError(key));
167174
}
168175
this.pendingRequests.clear();
169176

0 commit comments

Comments
 (0)