February 19, 2019·4 min read·engineeringfishshellpaths

fish 3.0.2 checks the working directory it inherits

A startup fix rejects stale PWD values without discarding a valid logical path through symlinks.

A terminal launched inside an editor can inherit two different accounts of its current location. The process has an actual working directory, while its environment also contains the string PWD. Those can disagree if the launcher changes the directory without updating the inherited environment. A shell that accepts the string unconditionally can start with an incorrect account of where it is.

The fish 3.0.2 release on February 19 fixes this startup behavior. Its release note says fish now ignores an inherited PWD when it does not resolve to the true working directory. The affected cases include terminals started by editors and IDEs.

I like fish's emphasis on a useful interactive experience without extensive configuration. That includes basic information about the current directory. Completions and prompts are less useful when the shell's recorded location begins with an unchecked value supplied by another process.

Why the inherited value existed

Always discarding PWD would lose legitimate information. A directory can be reached through a symbolic link, and a shell can preserve that logical path even when a physical path lookup produces a different spelling. The two names can identify the same directory.

The fish 3.0.1 startup code explicitly respects an inherited virtual path. If the environment contains a nonempty PWD, it installs that value as the shell's exported global directory variable. Only a missing or empty value triggers a lookup of the process's current directory.

That rule preserves logical paths, but it does not establish that the inherited path has anything to do with the current directory. An old value pointing to a different existing directory still passes the nonempty check. The shell needs to distinguish a different name for the same location from a name for another location.

This is an important limit on string comparison. Comparing the inherited text with a physical path spelling would reject valid symbolic-link paths. Accepting any nonempty text avoids that rejection but also accepts stale state. The correction needs to check what the paths identify.

The startup check compares directory identity

The 3.0.2 implementation converts the incoming value into fish's internal string representation. It keeps the value only when it is nonempty and paths_are_same_file considers it equivalent to the current directory, represented by .. Otherwise it sets PWD from the current-directory lookup.

The helper's implementation in path.cpp first checks path equivalence. If that does not establish a match, it obtains filesystem information for both paths and compares their device and inode identifiers. A match on both identifies the same filesystem object for this check.

This lets a valid logical path survive startup. A symbolic link can give the directory a different textual name while resolving to the same object. A stale path to a different directory fails the identity check, and fish reconstructs its directory variable from the process's actual location.

The change does not move the process to the inherited directory. It repairs the shell variable when that variable is inconsistent with the location the process already has. That is the appropriate direction for a terminal launcher case, where the new terminal may intentionally start in a different directory from its parent application.

Logical and physical output remain distinct

The pwd builtin in this release reads fish's PWD value. Its default behavior preserves the logical path, while -P resolves symbolic links. These commands therefore ask related but different questions.

pwd
pwd -P

In a directory reached through a symlink, the outputs can have different spellings. That alone is not evidence of the startup bug. The important condition is whether the paths refer to the same directory, which is exactly what the new startup check tries to establish.

The builtin source also shows why the inherited value matters beyond an ornamental prompt. Its logical output comes from the shell variable. If that variable is wrong, asking for the logical working directory repeats the wrong information. Resolving the stored path physically does not make it the process's actual location when the stored path points somewhere else.

The fix belongs before interactive configuration

The validation happens during environment initialization. That places the correction before the shell's normal use of its initialized environment. A prompt function should not need to compensate for a stale inherited directory, and a terminal integration should not need a fish-specific command simply to make the shell recognize its starting location.

The implementation also treats PWD as read-only to ordinary variable assignments. It is a maintained description of shell state, not an unrestricted user setting. Startup still has to inspect the incoming environment because a parent process can supply it before fish applies its own variable rules.

I prefer that division of responsibility. A launcher should supply a consistent environment, but fish can verify the directory information it receives without discarding valid logical paths. The patch improves behavior for inconsistent launchers while retaining the reason that accepting an inherited path was useful in the first place.

Version 3.0.2 is a narrow patch for an issue discovered in 3.0.1. Its check accepts a logical path when it identifies the current directory and falls back to the actual working-directory lookup otherwise. Editor-launched terminals can consequently start with a consistent directory variable while ordinary symbolic-link navigation retains its logical spelling.