{"record":{"id":"26143814e532db9c","repo":"microsoft/TypeScript","slug":"invalid-name-for-template-cancellation-pipe-it-sh","errorCode":null,"errorMessage":"Invalid name for template cancellation pipe: it should have length greater than 2 characters and contain only one '*'.","messagePattern":"Invalid name for template cancellation pipe: it should have length greater than 2 characters and contain only one '\\*'\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/tsserver/nodeServer.ts","lineNumber":695,"sourceCode":"    let cancellationPipeName: string | undefined;\r\n    for (let i = 0; i < args.length - 1; i++) {\r\n        if (args[i] === \"--cancellationPipeName\") {\r\n            cancellationPipeName = args[i + 1];\r\n            break;\r\n        }\r\n    }\r\n    if (!cancellationPipeName) {\r\n        return ts.server.nullCancellationToken;\r\n    }\r\n    // cancellationPipeName is a string without '*' inside that can optionally end with '*'\r\n    // when client wants to signal cancellation it should create a named pipe with name=<cancellationPipeName>\r\n    // server will synchronously check the presence of the pipe and treat its existence as indicator that current request should be canceled.\r\n    // in case if client prefers to use more fine-grained schema than one name for all request it can add '*' to the end of cancellationPipeName.\r\n    // in this case pipe name will be build dynamically as <cancellationPipeName><request_seq>.\r\n    if (cancellationPipeName.charAt(cancellationPipeName.length - 1) === \"*\") {\r\n        const namePrefix = cancellationPipeName.slice(0, -1);\r\n        if (namePrefix.length === 0 || namePrefix.includes(\"*\")) {\r\n            throw new Error(\"Invalid name for template cancellation pipe: it should have length greater than 2 characters and contain only one '*'.\");\r\n        }\r\n        let perRequestPipeName: string | undefined;\r\n        let currentRequestId: number;\r\n        return {\r\n            isCancellationRequested: () => perRequestPipeName !== undefined && pipeExists(perRequestPipeName),\r\n            setRequest(requestId: number) {\r\n                currentRequestId = requestId;\r\n                perRequestPipeName = namePrefix + requestId;\r\n            },\r\n            resetRequest(requestId: number) {\r\n                if (currentRequestId !== requestId) {\r\n                    throw new Error(`Mismatched request id, expected ${currentRequestId}, actual ${requestId}`);\r\n                }\r\n                perRequestPipeName = undefined;\r\n            },\r\n        };\r\n    }\r\n    else {\r","sourceCodeStart":677,"sourceCodeEnd":713,"githubUrl":"https://github.com/microsoft/TypeScript/blob/b465fdbfe175304d9b977da137b2c178ae1091d3/src/tsserver/nodeServer.ts#L677-L713","documentation":"Thrown while parsing the --cancellationPipeName command-line argument in tsserver/nodeServer's createCancellationToken. When the supplied name ends with '*', tsserver treats it as a per-request template: the prefix (everything before the final '*') is concatenated with each request sequence to form per-request named-pipe names. The prefix must be non-empty and must not itself contain another '*'. This guards against degenerate/malformed inputs like \"*\", \"**\", or \"a*b*\" that would produce empty or ambiguous pipe names. Note the message text says 'length greater than 2 characters', but the code actually enforces only namePrefix.length > 0 and no embedded '*'.","triggerScenarios":"Launching tsserver/nodeServer with `--cancellationPipeName \"*\"` (prefix empty), `--cancellationPipeName \"**\"` (prefix is '*' which includes '*'), or any value whose trailing '*' is preceded by another '*' or nothing. The check fires at server startup inside createCancellationToken, before any request is served.","commonSituations":"Editor/IDE integrations (VS Code, Vim/CoC, Emacs lsp-mode, custom LSP clients) that pass --cancellationPipeName with a templated suffix and mis-build the value — e.g. appending '*' to an empty base, double-escaping the '*', or quoting the arg incorrectly so only '*' reaches the arg parser. Copy-paste errors between configs that previously used a fixed (non-templated) pipe name.","solutions":["Provide a non-empty prefix with exactly one trailing '*', e.g. --cancellationPipeName \"\\\\\\\\.\\\\pipe\\\\ts-cancellation-*\" so the per-request pipe becomes <prefix><request_seq>.","If you do not need per-request cancellation, drop the trailing '*' entirely and pass a plain pipe name; tsserver will use one shared pipe for all requests.","Validate the argument before launching tsserver: length > 1, exactly one '*' and it must be the last character, and the remainder non-empty.","Remove the --cancellationPipeName flag to disable pipe-based cancellation entirely (tsserver falls back to ts.server.nullCancellationToken)."],"exampleFix":"// before\n// --cancellationPipeName \"*\"   // throws: empty prefix\n\n// after (per-request template)\n// --cancellationPipeName \"\\\\\\\\.\\\\pipe\\\\vtl-cancellation-*\"\n\n// after (single shared pipe, no template)\n// --cancellationPipeName \"\\\\\\\\.\\\\pipe\\\\vtl-cancellation\"","handlingStrategy":"validation","validationCode":"// Validate the --cancellationPipeName value before spawning tsserver.\nfunction isValidCancellationPipeName(name: string): boolean {\n  if (!name.endsWith(\"*\")) return true;                 // fixed pipe name is fine\n  const prefix = name.slice(0, -1);\n  return prefix.length > 0 && !prefix.includes(\"*\");    // non-empty, no stray '*'\n}\nif (!isValidCancellationPipeName(cancellationPipeName)) {\n  throw new Error(`Bad --cancellationPipeName: ${cancellationPipeName}`);\n}","typeGuard":"function isTemplateCancellationPipeName(name: string): boolean {\n  return name.endsWith(\"*\");\n}\nfunction templatePrefix(name: string): string | null {\n  if (!isTemplateCancellationPipeName(name)) return null;\n  const prefix = name.slice(0, -1);\n  return prefix.length > 0 && !prefix.includes(\"*\") ? prefix : null;\n}","tryCatchPattern":"// This throws during tsserver startup (before requests), so a runtime catch inside\n// the client is not useful. Validate the argument client-side before launching.","preventionTips":["Treat --cancellationPipeName as structured input: a non-empty base plus at most one trailing '*'.","Prefer a fully-qualified pipe path (e.g. \\\\\\\\.\\\\pipe\\\\<name>[-*]) to avoid empty prefixes.","Drop the trailing '*' (or the whole flag) if per-request cancellation is not required.","Add an integration test that asserts the spawned tsserver starts cleanly with your cancellation args."],"tags":["tsserver","named-pipe","cancellation","command-line","windows"],"backgroundTag":null,"analyzedSha":"b465fdbfe175304d9b977da137b2c178ae1091d3","analyzedAt":"2026-08-12T05:38:42.698Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}