microsoft/typescript-go · error

invalid file name: ${fileName}

Error message

invalid file name: ${fileName}

What it means

fileNameToDocumentURI converts the internal dynamic-file-name format '^/scheme/authority/path' back into a URI. After stripping the '^/' prefix, it requires at least one '/' so a scheme can be split from the remainder. A name like '^/untitled' (scheme only, nothing after the first slash) has no authority/path component and cannot be mapped to a URI, so it throws.

Source

Thrown at _packages/native-preview/src/api/path.ts:451

 * @example
 * fileNameToDocumentURI("/path/to/file.ts") === "file:///path/to/file.ts"
 * fileNameToDocumentURI("c:/path/to/file.ts") === "file:///c%3A/path/to/file.ts"
 * fileNameToDocumentURI("^/untitled/ts-nul-authority/Untitled-1") === "untitled:Untitled-1"
 * fileNameToDocumentURI("^/vscode-vfs/github/microsoft/typescript-go/file.ts") === "vscode-vfs://github/microsoft/typescript-go/file.ts"
 */
export function fileNameToDocumentURI(fileName: string): string {
    // Bundled files are returned as-is
    if (isBundled(fileName)) {
        return fileName;
    }

    // Dynamic/virtual files (untitled, vscode-vfs, etc.) need special handling
    if (isDynamicFileName(fileName)) {
        // Format: ^/scheme/authority/path
        const withoutPrefix = fileName.substring(2); // Remove "^/"
        const firstSlash = withoutPrefix.indexOf("/");
        if (firstSlash === -1) {
            throw new Error("invalid file name: " + fileName);
        }
        const scheme = withoutPrefix.substring(0, firstSlash);
        const rest = withoutPrefix.substring(firstSlash + 1);

        const secondSlash = rest.indexOf("/");
        if (secondSlash === -1) {
            throw new Error("invalid file name: " + fileName);
        }
        const authority = rest.substring(0, secondSlash);
        const path = rest.substring(secondSlash + 1);

        // ts-nul-authority is a placeholder for URIs without an authority
        if (authority === "ts-nul-authority") {
            return scheme + ":" + path;
        }
        return scheme + "://" + authority + "/" + path;
    }

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Ensure dynamic file names use the full format '^/scheme/authority/path' (authority is 'ts-nul-authority' when there is none)
  2. Produce dynamic names via documentURIToFileName(uri) instead of building them manually so they always round-trip
  3. Guard inputs with a regex such as /^\/[^/]+\/[^/]+\// before calling fileNameToDocumentURI

Example fix

// before
fileNameToDocumentURI("^/untitled"); // throws: no slash after scheme

// after
fileNameToDocumentURI("^/untitled/ts-nul-authority/Untitled-1"); // 'untitled:Untitled-1'
Defensive patterns

Strategy: validation

Validate before calling

const validDynamicName = (f: string) => /^\^\/[^/]+\/[^/]+\//.test(f);

Type guard

function isValidDynamicFileName(f: string): boolean {
  return !f.startsWith("^/") || /^\^\/[^/]+\/[^/]+\//.test(f);
}

Try / catch

try { uri = fileNameToDocumentURI(name); } catch (e) { if ((e as Error).message.startsWith("invalid file name")) { /* repair name to ^/scheme/authority/path */ } else throw e; }

Prevention

When it happens

Trigger: Calling fileNameToDocumentURI with a dynamic file name that starts with '^/' but contains no '/' after the scheme: '^/untitled', '^/vscode-vfs', '^/memory'. Well-formed inputs like '^/untitled/ts-nul-authority/Untitled-1' pass because they have two slashes.

Common situations: Editor integrations constructing dynamic file names by hand instead of round-tripping through documentURIToFileName; string concatenation bugs that drop the authority/path segments; custom virtual file schemes registered without a path part.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/4c2bba16fd65d5fe. Report an issue: GitHub.