microsoft/typescript-go · error

unsupported file extension: %s

Error message

unsupported file extension: %s

What it means

Returned by the project API file-update path when a newly opened/updated overlay file's extension maps to core.ScriptKindUnknown. The overlay's script kind is derived from the file name via GetScriptKindFromFileName, which only recognizes .js/.cjs/.mjs/.jsx/.ts/.cts/.mts/.tsx/.json (case-insensitive); anything else — no extension, .vue, .svelte, .txt — cannot be assigned a parser kind and the snapshot update is refused. Note this only applies to files not already in the overlay set; existing overlays reuse their recorded kind.

Source

Thrown at internal/project/api.go:56

// An error is returned if the file name does not have a recognized script extension.
// On success, the returned snapshot carries a single reference (the clone ref);
// the caller must call snapshot.Deref(s) when done.
func (s *Session) APIUpdateTemporary(ctx context.Context, baseSnapshot *Snapshot, uri lsproto.DocumentUri, newText string) (*Snapshot, error) {
	path := uri.Path(baseSnapshot.UseCaseSensitiveFileNames())

	overlays := maps.Clone(baseSnapshot.fs.overlays)
	version := int32(0)
	var fileChanges FileChangeSummary
	existing := overlays[path]
	var scriptKind core.ScriptKind
	if existing != nil {
		version = existing.Version() + 1
		scriptKind = existing.Kind()
		fileChanges.Changed.Add(uri)
	} else {
		scriptKind = core.GetScriptKindFromFileName(uri.FileName())
		if scriptKind == core.ScriptKindUnknown {
			return nil, fmt.Errorf("unsupported file extension: %s", uri.FileName())
		}
		fileChanges.Opened = uri
	}
	overlays[path] = newOverlay(uri.FileName(), newText, version, scriptKind)

	newSnapshot := baseSnapshot.Clone(ctx, SnapshotChange{
		fileChanges: fileChanges,
		ResourceRequest: ResourceRequest{
			Documents: []lsproto.DocumentUri{uri},
		},
	}, overlays, s)
	return newSnapshot, nil
}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Only open/update files with .ts/.tsx/.mts/.cts/.js/.jsx/.mjs/.cjs/.json extensions
  2. Filter non-TS documents before forwarding them from an editor integration
  3. For framework files (.vue/.svelte), extract the script block into a virtual .ts/.js URI before handing it over
  4. Check the last suffix of the path client-side if uncertain

Example fix

// before
snap, err := proj.UpdateFile(ctx, uri "/src/Component.vue", text)

// after
if core.GetScriptKindFromFileName(name) == core.ScriptKindUnknown {
	return fmt.Errorf("skip non-TS file: %s", name)
}
snap, err := proj.UpdateFile(ctx, uri, text)
Defensive patterns

Strategy: type-guard

Type guard

func isSupportedScriptFile(name string) bool {
	switch strings.ToLower(filepath.Ext(name)) {
	case ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json":
		return true
	}
	return false
}

Try / catch

snap, err := proj.UpdateFile(ctx, uri, text)
if err != nil {
	if strings.Contains(err.Error(), "unsupported file extension") {
		return snap, nil // not a script file; skip silently
	}
	return nil, err
}

Prevention

When it happens

Trigger: Calling the API open/update with a .svelte, .vue, .astro, .md, or extensionless file; paths with uppercase extensions are fine (ToLower applied) but double extensions like .d.json style oddities still route by last suffix; HTML entry files passed as documents.

Common situations: Embedding the language service into frameworks whose source files carry non-TS extensions; tooling that mirrors all editor open documents (including README/HTML) into the API; scripts passing paths without extensions.

Related errors


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