From 1809681c98d653b4923dc4cb3301e2026bdb2023 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:49:53 +0000 Subject: [PATCH 1/4] Initial plan From 4b68aa8aeb1065c90ddc180ae42a699d0b717490 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 22:11:00 +0000 Subject: [PATCH 2/4] Suppress UNC host access errors from unhandled-error telemetry Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/ced760c1-3e96-49dc-9650-5a4b1ea18dcb Co-authored-by: bryanchen-d <41454397+bryanchen-d@users.noreply.github.com> --- src/vs/base/common/errors.ts | 12 ++++++++++++ src/vs/base/test/common/errors.test.ts | 17 ++++++++++++++++- .../files/node/diskFileSystemProvider.ts | 18 ++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 69645c4c57447..9a93845efa12e 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -324,6 +324,18 @@ export class ErrorNoTelemetry extends Error { } } +/** + * Marks an error so that {@link ErrorNoTelemetry.isErrorNoTelemetry} detects it + * and the error telemetry pipeline does not report it as an unhandled error. + * Useful when the original error class (e.g. `FileSystemProviderError`) needs + * to be preserved for callers but the error itself represents an expected + * condition that should not surface in error telemetry. + */ +export function markAsErrorNoTelemetry(error: T): T { + error.name = 'CodeExpectedError'; + return error; +} + /** * This error indicates a bug. * Do not throw this for invalid user input. diff --git a/src/vs/base/test/common/errors.test.ts b/src/vs/base/test/common/errors.test.ts index 9dec9b4d26ad7..6f1cbaaa654fb 100644 --- a/src/vs/base/test/common/errors.test.ts +++ b/src/vs/base/test/common/errors.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { toErrorMessage } from '../../common/errorMessage.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { transformErrorForSerialization, transformErrorFromSerialization } from '../../common/errors.js'; +import { ErrorNoTelemetry, markAsErrorNoTelemetry, transformErrorForSerialization, transformErrorFromSerialization } from '../../common/errors.js'; import { assertType } from '../../common/types.js'; suite('Errors', () => { @@ -84,4 +84,19 @@ suite('Errors', () => { assert.strictEqual(deserializedError.cause?.message, 'Cause error'); assert.strictEqual(deserializedError.cause?.stack, serializedCause.stack); }); + + test('markAsErrorNoTelemetry preserves error type while making it no-telemetry', function () { + class MyError extends Error { + readonly code = 'MY_CODE'; + } + + const error = new MyError('boom'); + const marked = markAsErrorNoTelemetry(error); + + assert.strictEqual(marked, error, 'returns the same error instance'); + assert.ok(error instanceof MyError, 'preserves the original error class'); + assert.strictEqual(error.code, 'MY_CODE', 'preserves additional properties'); + assert.strictEqual(error.message, 'boom', 'preserves the message'); + assert.ok(ErrorNoTelemetry.isErrorNoTelemetry(error), 'is detected as ErrorNoTelemetry'); + }); }); diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 2e2e64bd6220a..b6a32890848be 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -24,6 +24,7 @@ import { AbstractNonRecursiveWatcherClient, AbstractUniversalWatcherClient, ILog import { AbstractDiskFileSystemProvider } from '../common/diskFileSystemProvider.js'; import { UniversalWatcherClient } from './watcher/watcherClient.js'; import { NodeJSWatcherClient } from './watcher/nodejs/nodejsClient.js'; +import { markAsErrorNoTelemetry } from '../../../base/common/errors.js'; export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, @@ -846,6 +847,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple let resultError: Error | string = error; let code: FileSystemProviderErrorCode; + let isExpected = false; switch (error.code) { case 'ENOENT': code = FileSystemProviderErrorCode.FileNotFound; @@ -865,13 +867,25 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple break; case 'ERR_UNC_HOST_NOT_ALLOWED': resultError = `${error.message}. Please update the 'security.allowedUNCHosts' setting if you want to allow this host.`; - code = FileSystemProviderErrorCode.Unknown; + // Treat UNC host restriction as a permission denial: it is a + // security policy preventing access to the host, not an + // unknown failure. The error is also marked as expected so it + // is not reported as an unhandled error in telemetry — the + // host allowlist is user-configured and access denials are a + // normal outcome rather than a bug. + code = FileSystemProviderErrorCode.NoPermissions; + isExpected = true; break; default: code = FileSystemProviderErrorCode.Unknown; } - return createFileSystemProviderError(resultError, code); + const providerError = createFileSystemProviderError(resultError, code); + if (isExpected) { + markAsErrorNoTelemetry(providerError); + } + + return providerError; } private async toFileSystemProviderWriteError(resource: URI | undefined, error: NodeJS.ErrnoException): Promise { From 0ad9bb6d3cb64adddc931c0fd33fa4e08dcff6f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:24:15 +0000 Subject: [PATCH 3/4] Use a flag instead of mutating error.name in markAsErrorNoTelemetry Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/5fe8b3aa-6580-4b37-b5e8-3c4a11350ee2 Co-authored-by: bryanchen-d <41454397+bryanchen-d@users.noreply.github.com> --- src/vs/base/common/errors.ts | 15 ++++++++++++--- src/vs/base/test/common/errors.test.ts | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 9a93845efa12e..4cb4ea992869b 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -297,6 +297,8 @@ export class ExpectedError extends Error { readonly isExpected = true; } +const noTelemetryFlag = '__vscodeNoTelemetry'; + /** * Error that when thrown won't be logged in telemetry as an unhandled error. */ @@ -306,6 +308,8 @@ export class ErrorNoTelemetry extends Error { constructor(msg?: string) { super(msg); this.name = 'CodeExpectedError'; + // eslint-disable-next-line local/code-no-any-casts + (this)[noTelemetryFlag] = true; } public static fromError(err: Error): ErrorNoTelemetry { @@ -320,7 +324,8 @@ export class ErrorNoTelemetry extends Error { } public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry { - return err.name === 'CodeExpectedError'; + // eslint-disable-next-line local/code-no-any-casts + return err.name === 'CodeExpectedError' || (err)?.[noTelemetryFlag] === true; } } @@ -329,10 +334,14 @@ export class ErrorNoTelemetry extends Error { * and the error telemetry pipeline does not report it as an unhandled error. * Useful when the original error class (e.g. `FileSystemProviderError`) needs * to be preserved for callers but the error itself represents an expected - * condition that should not surface in error telemetry. + * condition that should not surface in error telemetry. Unlike using + * {@link ErrorNoTelemetry} directly, this preserves the original error's + * `name`, prototype chain, and any additional properties (such as the error + * code that some IPC channels carry through `Error#name`). */ export function markAsErrorNoTelemetry(error: T): T { - error.name = 'CodeExpectedError'; + // eslint-disable-next-line local/code-no-any-casts + (error)[noTelemetryFlag] = true; return error; } diff --git a/src/vs/base/test/common/errors.test.ts b/src/vs/base/test/common/errors.test.ts index 6f1cbaaa654fb..00df5ba1202e7 100644 --- a/src/vs/base/test/common/errors.test.ts +++ b/src/vs/base/test/common/errors.test.ts @@ -87,6 +87,7 @@ suite('Errors', () => { test('markAsErrorNoTelemetry preserves error type while making it no-telemetry', function () { class MyError extends Error { + override readonly name = 'MyError'; readonly code = 'MY_CODE'; } @@ -95,6 +96,7 @@ suite('Errors', () => { assert.strictEqual(marked, error, 'returns the same error instance'); assert.ok(error instanceof MyError, 'preserves the original error class'); + assert.strictEqual(error.name, 'MyError', 'preserves the original error name'); assert.strictEqual(error.code, 'MY_CODE', 'preserves additional properties'); assert.strictEqual(error.message, 'boom', 'preserves the message'); assert.ok(ErrorNoTelemetry.isErrorNoTelemetry(error), 'is detected as ErrorNoTelemetry'); From 6f3dfaf5d3ddcc66ceba638d8e39c904d450fff8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 00:10:35 +0000 Subject: [PATCH 4/4] Use typed cast instead of `any` for noTelemetryFlag Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/94e6d14a-9e39-41af-b69e-b2a6a715e8d5 Co-authored-by: bryanchen-d <41454397+bryanchen-d@users.noreply.github.com> --- src/vs/base/common/errors.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 4cb4ea992869b..7cee96244a0dc 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -298,6 +298,7 @@ export class ExpectedError extends Error { } const noTelemetryFlag = '__vscodeNoTelemetry'; +type ErrorWithNoTelemetryFlag = Error & { [noTelemetryFlag]?: boolean }; /** * Error that when thrown won't be logged in telemetry as an unhandled error. @@ -308,8 +309,7 @@ export class ErrorNoTelemetry extends Error { constructor(msg?: string) { super(msg); this.name = 'CodeExpectedError'; - // eslint-disable-next-line local/code-no-any-casts - (this)[noTelemetryFlag] = true; + (this)[noTelemetryFlag] = true; } public static fromError(err: Error): ErrorNoTelemetry { @@ -324,8 +324,7 @@ export class ErrorNoTelemetry extends Error { } public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry { - // eslint-disable-next-line local/code-no-any-casts - return err.name === 'CodeExpectedError' || (err)?.[noTelemetryFlag] === true; + return err.name === 'CodeExpectedError' || (err)?.[noTelemetryFlag] === true; } } @@ -340,8 +339,7 @@ export class ErrorNoTelemetry extends Error { * code that some IPC channels carry through `Error#name`). */ export function markAsErrorNoTelemetry(error: T): T { - // eslint-disable-next-line local/code-no-any-casts - (error)[noTelemetryFlag] = true; + (error)[noTelemetryFlag] = true; return error; }