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

  1. Send the file exactly as the server reports it: call getSourceFileNames and reuse one of those names (or send {uri: ...} consistently)
  2. If sending a plain string, make it absolute and normalized against the server's Cwd
  3. Open the file first via updateSnapshot (openFiles) so the program actually contains it
  4. 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

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


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