microsoft/typescript-go · error · ErrClientError
%w: source file not found: %v
Error message
%w: source file not found: %v
What it means
resolveLocation was given a file+position (no node handle) and program.GetSourceFile(name) returned nil: "source file not found: <file>". The lookup uses DocumentIdentifier.ToFileName(), which returns the URI's file name when a {uri} object was sent, or the FileName string verbatim otherwise - a plain string is NOT normalized to an absolute path. Programs key source files by absolute normalized paths, so any relative/cased-differently/foreign path misses, as does a file that simply is not part of this project's program in this snapshot.
Source
Thrown at internal/api/session.go:530
func (setup checkerSetup) resolveSymbolHandle(id SymbolID) (*ast.Symbol, error) {
return setup.sd.resolveSymbolHandle(id)
}
func (setup checkerSetup) resolveSignatureHandle(id SignatureID) (*checker.Signature, error) {
return setup.sd.resolveSignatureHandle(setup.projectID, id)
}
// resolveLocation resolves an optional location, given either as a node handle or as a
// file and position. Returns nil when neither is provided.
func (setup checkerSetup) resolveLocation(handle NodeHandle, file *DocumentIdentifier, position *uint32) (*ast.Node, error) {
if handle != "" {
return setup.sd.resolveNodeHandle(setup.program, handle)
}
if file != nil && position != nil {
sourceFile := setup.program.GetSourceFile(file.ToFileName())
if sourceFile == nil {
return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, *file)
}
return astnav.GetTouchingPropertyName(sourceFile, sourceFile.GetPositionMap().UTF16ToUTF8(int(*position))), nil
}
return nil, nil
}
// setupChecker resolves snapshot, program, and type checker for a project.
// Callers must defer setup.done() to release the checker.
func (s *Session) setupChecker(ctx context.Context, snapshot SnapshotID, projectHandle ProjectID) (checkerSetup, error) {
sd, err := s.getSnapshotData(snapshot)
if err != nil {
return checkerSetup{}, err
}
program, err := sd.getProgram(projectHandle)
if err != nil {
return checkerSetup{}, err
}View on GitHub (pinned to 1bcfa18d79)
Solutions
- Send the file exactly as the server reports it: call getSourceFileNames and reuse one of those names (or send {uri: ...} consistently)
- If sending a plain string, make it absolute and normalized against the server's Cwd
- Open the file first via updateSnapshot (openFiles) so the program actually contains it
- Verify you are querying the project that owns the file (getDefaultProjectForFile)
Example fix
// before
await call("getTypeAtLocation", { snapshot, project, file: "src/index.ts", position }); // relative -> not found
// after
await call("getTypeAtLocation", { snapshot, project, file: path.resolve(cwd, "src/index.ts"), position });
// or: file: { uri: "file:///abs/path/src/index.ts" } Defensive patterns
Strategy: validation
Validate before calling
// TS client: validate the file against the program's known names before querying.
const names: string[] = await call("getSourceFileNames", { snapshot, project });
const known = new Set(names);
const fileToUse = known.has(file) || known.has(file.replace(/\\/g, "/"));
if (!fileToUse) {
throw new Error(`${file} is not in the program; open it via updateSnapshot or pass an absolute path/{uri}`);
}
await call("getTypeAtLocation", { snapshot, project, file, position }); Type guard
const isProgramFile = (f: string, programFiles: Set<string>): boolean => programFiles.has(f) || programFiles.has(f.replace(/\\/g, "/"));
Try / catch
try {
return await call(method, params);
} catch (e) {
if (String(e).includes("source file not found")) {
// normalize and retry once: absolute path or {uri} form
const abs = path.isAbsolute(params.file) ? params.file : path.resolve(cwd, params.file);
return await call(method, { ...params, file: { uri: `file://${abs}` } });
}
throw e;
} Prevention
- Reuse file names verbatim from getSourceFileNames responses
- Send plain strings as absolute normalized paths, or consistently use the {uri} form
- Open files via updateSnapshot before first query on a new/renamed file
- Query the project that owns the file (getDefaultProjectForFile)
When it happens
Trigger: getTypeAtLocation/getSymbolAtPosition/getSymbolsInScope with file: "src/index.ts" (relative) instead of the absolute path; passing a URI-shaped string where a plain absolute path was expected (or vice versa); querying a file that was never opened via updateSnapshot or not included by the project's tsconfig; using a file name from a different project's program; case mismatch on case-sensitive filesystems.
Common situations: Clients deriving file names from their own relative paths instead of server-returned names; forgetting to open a newly created file before querying it; monorepo files claimed by a different project than the handle passed; editor URIs (file:///...) round-tripped inconsistently between string and {uri} forms.
Related errors
- invalid file name: ${fileName}
- invalid file URI: ${uri}
- invalid URI: ${uri}
- %w: project has no program
- %w: empty symbol handle
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/5d44a65d58f44697.
Report an issue: GitHub.