[Bug] macOS Debugger Fails with UNABLE_TO_VERIFY_LEAF_SIGNATURE on HTTPS
Description
When attempting to debug a .NET Isolated Azure Function on macOS using HTTPS, the debugger fails to attach. The preLaunchTask successfully starts the function host, but the extension's internal health check to the /admin/host/status endpoint times out after 60 seconds, throwing a RestError: UNABLE_TO_VERIFY_LEAF_SIGNATURE.
This occurs even when all local configurations are correct, pointing to a bug in how the extension's runtime environment handles TLS connections on macOS.
Key Findings
A detailed investigation revealed the following:
- HTTP Works: Switching the debug task to use HTTP works perfectly, and the debugger attaches instantly.
-
curlWorks: While the extension is failing, a manualcurl https://localhost:<port>/admin/host/statuscommand from the terminal succeeds and returns a "Running" state. -
Certificate is Trusted: The development certificate has been explicitly trusted via
dotnet dev-certs https --trustand is verified as trusted in the macOS Keychain. - No Environmental Blocks: The issue persists with the macOS firewall turned off and no other security software or proxies configured.
Root Cause Analysis
An analysis of the extension's source code shows that the startFuncTask function correctly attempts to disable certificate validation for the localhost health check. It explicitly sets the { rejectUnauthorized: false } option before calling sendRequestWithTimeout.
Despite this correct implementation, the underlying HTTP request still fails with a certificate validation error. This proves that the rejectUnauthorized: false flag is not being honored by the underlying execution stack (either the specific version of Node.js used by VS Code or the @azure/core-rest-pipeline library) on macOS.
Like because rejectUnauthorized is honored by underlying library when it's value is true.
export async function sendRequestWithTimeout(context: IActionContext, options: types.AzExtRequestPrepareOptions, timeout: number, clientInfo: types.AzExtGenericClientInfo): Promise<types.AzExtPipelineResponse> {
const request: PipelineRequest = createPipelineRequest({
...options,
timeout
});
if (options.rejectUnauthorized) {
request.agent = new HttpsAgent({ rejectUnauthorized: options.rejectUnauthorized });
}
const client = await createGenericClient(context, clientInfo, { noRetryPolicy: true, addStatusCodePolicy: true });
return await client.sendRequest(request);
}
Environment
OS Version: MacOS 15.6 (24G84) VS Code Version: 1.102.1 (Universal) Extension Version: 1.18.0 (current) .NET SDK Version: 9.0.303 Core Tools Version: 4.1.0+7ff2567e43c1ae7471cea7394452c1447661e175 (64-bit) Function Runtime Version: 4.1040.300.25317
Location:
startFuncTask (/Users/***/Code/vscode-azurefunctions/src/commands/pickFuncProcess.ts:158)
Error:
{
name: "RestError",
code: "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
statusCode: undefined,
request: {
url: "https://localhost:<port>/admin/host/status",
body: undefined,
headers: {
_headersMap: {
},
},
method: "GET",
timeout: 500,
multipartBody: undefined,
formData: undefined,
disableKeepAlive: false,
proxySettings: undefined,
streamResponseStatusCodes: undefined,
withCredentials: false,
abortSignal: undefined,
tracingOptions: undefined,
onUploadProgress: undefined,
onDownloadProgress: undefined,
requestId: "d86cf8cc-ebe4-460a-a9f4-33d711eab2ec",
allowInsecureConnection: true,
enableBrowserStreams: false,
agent: undefined,
tlsSettings: undefined,
}
}
Error Stack Trace:
RestError: unable to verify the first certificate
at ClientRequest.<anonymous> (/Users/***/Code/vscode-azurefunctions/node_modules/@azure/core-rest-pipeline/src/nodeHttpClient.ts:241:11)
at Object.onceWrapper (node:events:633:26)
at ClientRequest.emit (node:events:518:28)
at emitErrorEvent (node:_http_client:104:11)
at TLSSocket.socketErrorListener (node:_http_client:518:5)
at TLSSocket.emit (node:events:518:28)
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at processTicksAndRejections (node:internal/process/task_queues:90:21)