microsoft / microsoft/vscode

Terminal: empty rename does not restore dynamic tab title — stale staticTitle is never cleared

Open
#333,933 0 comments 0 reactions 1 assignee Claimed by @meganrogge View on GitHub
bug confirmation-pending terminal-tabs
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: Yes (core terminal behavior)

- VS Code Version: 1.135.0 (08d4889f9ec4a1685d257b9b95de036c8e1ce1e5)
- OS Version: macOS (Darwin 25.5.0)

### Summary

Renaming a terminal tab to an **empty name** is supposed to clear the custom (static) title and restore the dynamic title (process/sequence-based, including the agentic CLI live title from `terminal.integrated.tabs.allowAgentCliTitle`). In practice the dynamic title reappears only for a moment and then reverts to the previously set custom name on the next title update from the shell/CLI.

Root cause: `_staticTitle` is never cleared on the empty-rename path.

### Steps to Reproduce

1. Open an integrated terminal running an agentic CLI (e.g. Claude Code) — with the default `terminal.integrated.tabs.allowAgentCliTitle: true`, the tab title live-updates via the escape-sequence title (`${sequence}`).
2. Right-click the terminal tab → **Rename** → enter `my-agent` → Enter. The tab now shows the static name (expected).
3. Rename again, clear the input to empty, press Enter.
4. The tab briefly shows the dynamic title again, but as soon as the CLI emits its next title escape sequence, the tab **reverts to `my-agent`**.

Expected: step 3 permanently removes the custom name and the tab follows the dynamic title again. The only workaround today is killing the terminal and opening a new one.

### Analysis

In `src/vs/workbench/contrib/terminal/browser/terminalInstance.ts`:

`rename()` maps an empty string to `undefined` and calls `_setTitle(undefined, TitleEventSource.Api)`:

```ts
async rename(title?: string, source?: TitleEventSource) {
if (title !== undefined && !title) {
title = undefined;
}
this._setTitle(title, source ?? TitleEventSource.Api);
}
```

`_updateTitleProperties()` returns early for `undefined` **before** reaching the `case TitleEventSource.Api:` branch that assigns `_staticTitle`, so the stale `_staticTitle` from the earlier rename survives (and `_titleSource` also stays `Api`):

```ts
private _updateTitleProperties(title: string | undefined, eventSource: TitleEventSource): string {
if (title === undefined) {
return this._processName; // <-- early return: _staticTitle NOT cleared
}
switch (eventSource) {
...
case TitleEventSource.Api:
this._staticTitle = title; // only reachable when title is defined
...
```

`_setTitle` then calls `refreshLabel(this, reset = true)`, and `computeLabel` bypasses the static title **once** because of the `!reset` guard:

```ts
if (!reset && instance.staticTitle && labelType === TerminalLabelType.Title) {
return instance.staticTitle... // stale staticTitle wins again on every later refresh
}
```

Every subsequent `refreshLabel` (triggered by process/sequence title events) runs with `reset === false`, hits the guard, and the stale `_staticTitle` wins again — producing the observed flash-then-revert behavior.

### Suggested fix

When `rename` is called with an empty/undefined title from the Api source, clear `_staticTitle` (and arguably reset `_titleSource` to `Process`) so later label refreshes fall through to the configured template / agent CLI sequence title.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.