Skip to content

runInDedicatedTerminal crashes when terminalKey is a string #1230

@eleanorjboyd

Description

@eleanorjboyd

Bug Description

runInDedicatedTerminal throws a TypeError when passed a string terminalKey, despite the API signature accepting Uri | string.

Error

TypeError: The "path" argument must be of type string or an instance of Buffer or URL. Received undefined
    at stat (node:fs:1678:16)
    at TerminalManagerImpl.getDedicatedTerminal (dist/extension.js:40773:37)
    at async PythonEnvironmentApiImpl.runInDedicatedTerminal (dist/extension.js:37630:26)

Root Cause

In src/features/terminal/terminalManager.ts, the getDedicatedTerminal method accesses .fsPath on terminalKey unconditionally (lines ~343-348):

const uriStat = await fsapi.stat(terminalKey.fsPath);  // Crashes when terminalKey is a string
const uriDir = uriStat.isDirectory() ? terminalKey.fsPath : path.dirname(terminalKey.fsPath);
...
const fileName = path.basename(terminalKey.fsPath).replace('.py', '');

The internal function signature declares terminalKey: Uri, but the public API signature accepts Uri | string:

// src/api.ts line 1132
runInDedicatedTerminal(
    terminalKey: Uri | string,
    environment: PythonEnvironment,
    options: PythonTerminalExecutionOptions,
): Promise<Terminal>;

When a string is passed (e.g., 'my-terminal-key'), the code tries to access .fsPath on a primitive string, which is undefined, causing fs.stat to throw.

Reproduction

const env = environments[0];
const options = { cwd: workspaceUri, args: ['--version'], show: false };

// This crashes:
await api.runInDedicatedTerminal('my-terminal-key', env, options);

// This works:
await api.runInDedicatedTerminal(vscode.Uri.file('/path/to/script.py'), env, options);

Suggested Fix

Handle the string case in getDedicatedTerminal:

async getDedicatedTerminal(
    terminalKey: Uri | string,  // Update type signature
    project: Uri | PythonProject,
    environment: PythonEnvironment,
    createNew: boolean = false,
): Promise<Terminal> {
    const part = terminalKey instanceof Uri ? path.normalize(terminalKey.fsPath) : terminalKey;
    const key = `${environment.envId.id}:${part}`;
    
    // ... existing cache lookup ...
    
    // Handle string keys differently - use project cwd instead of trying to stat the key
    const puri = project instanceof Uri ? project : project.uri;
    const config = getConfiguration('python', terminalKey instanceof Uri ? terminalKey : puri);
    const projectStat = await fsapi.stat(puri.fsPath);
    const projectDir = projectStat.isDirectory() ? puri.fsPath : path.dirname(puri.fsPath);
    
    let cwd: string;
    let name: string;
    
    if (terminalKey instanceof Uri) {
        const uriStat = await fsapi.stat(terminalKey.fsPath);
        const uriDir = uriStat.isDirectory() ? terminalKey.fsPath : path.dirname(terminalKey.fsPath);
        cwd = config.get<boolean>('terminal.executeInFileDir', false) ? uriDir : projectDir;
        const fileName = path.basename(terminalKey.fsPath).replace('.py', '');
        name = `Python: ${fileName}`;
    } else {
        cwd = projectDir;
        name = `Python: ${terminalKey}`;
    }
    
    // ... rest of implementation ...
}

Environment

  • Extension version: 1.19.0
  • VS Code version: 1.110.0-insider
  • OS: macOS (also reproducible on other platforms)

Additional Context

Discovered during integration testing. The tests runInDedicatedTerminal reuses terminal for same key and runInDedicatedTerminal uses different terminals for different keys fail with this error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue identified by VS Code Team member as probable bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions