{"record":{"id":"3eda3f70d4ae808c","repo":"microsoft/typescript-go","slug":"unsupported-file-extension-s","errorCode":null,"errorMessage":"unsupported file extension: %s","messagePattern":"unsupported file extension: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/project/api.go","lineNumber":56,"sourceCode":"// An error is returned if the file name does not have a recognized script extension.\n// On success, the returned snapshot carries a single reference (the clone ref);\n// the caller must call snapshot.Deref(s) when done.\nfunc (s *Session) APIUpdateTemporary(ctx context.Context, baseSnapshot *Snapshot, uri lsproto.DocumentUri, newText string) (*Snapshot, error) {\n\tpath := uri.Path(baseSnapshot.UseCaseSensitiveFileNames())\n\n\toverlays := maps.Clone(baseSnapshot.fs.overlays)\n\tversion := int32(0)\n\tvar fileChanges FileChangeSummary\n\texisting := overlays[path]\n\tvar scriptKind core.ScriptKind\n\tif existing != nil {\n\t\tversion = existing.Version() + 1\n\t\tscriptKind = existing.Kind()\n\t\tfileChanges.Changed.Add(uri)\n\t} else {\n\t\tscriptKind = core.GetScriptKindFromFileName(uri.FileName())\n\t\tif scriptKind == core.ScriptKindUnknown {\n\t\t\treturn nil, fmt.Errorf(\"unsupported file extension: %s\", uri.FileName())\n\t\t}\n\t\tfileChanges.Opened = uri\n\t}\n\toverlays[path] = newOverlay(uri.FileName(), newText, version, scriptKind)\n\n\tnewSnapshot := baseSnapshot.Clone(ctx, SnapshotChange{\n\t\tfileChanges: fileChanges,\n\t\tResourceRequest: ResourceRequest{\n\t\t\tDocuments: []lsproto.DocumentUri{uri},\n\t\t},\n\t}, overlays, s)\n\treturn newSnapshot, nil\n}\n","sourceCodeStart":38,"sourceCodeEnd":70,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/project/api.go#L38-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Only open/update files with .ts/.tsx/.mts/.cts/.js/.jsx/.mjs/.cjs/.json extensions","Filter non-TS documents before forwarding them from an editor integration","For framework files (.vue/.svelte), extract the script block into a virtual .ts/.js URI before handing it over","Check the last suffix of the path client-side if uncertain"],"exampleFix":"// before\nsnap, err := proj.UpdateFile(ctx, uri \"/src/Component.vue\", text)\n\n// after\nif core.GetScriptKindFromFileName(name) == core.ScriptKindUnknown {\n\treturn fmt.Errorf(\"skip non-TS file: %s\", name)\n}\nsnap, err := proj.UpdateFile(ctx, uri, text)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isSupportedScriptFile(name string) bool {\n\tswitch strings.ToLower(filepath.Ext(name)) {\n\tcase \".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\", \".json\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"snap, err := proj.UpdateFile(ctx, uri, text)\nif err != nil {\n\tif strings.Contains(err.Error(), \"unsupported file extension\") {\n\t\treturn snap, nil // not a script file; skip silently\n\t}\n\treturn nil, err\n}","preventionTips":["Filter documents to .ts/.tsx/.mts/.cts/.js/.jsx/.mjs/.cjs/.json before the API call","For .vue/.svelte sources, project the script block into a virtual .ts/.js path","Remember extension matching is case-insensitive on the last suffix"],"tags":["file-extension","overlay","validation","api"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}