Zellij, Fish Shell, and the Illusion of "The First Terminal"

Zellij, Fish Shell, and the Illusion of "The First Terminal"

Abstract

Zellij is often praised as a modern terminal multiplexer with a clean architecture and strong defaults.
However, when users attempt to automate Zellij—especially when integrating it with Fish shell—they frequently encounter confusing behavior: unexpected session sharing, nested Zellij instances, or race conditions during startup.

This article documents a real-world investigation into these issues and explains why certain intuitive solutions fail, what Zellij actually guarantees, and which design principles lead to a stable setup.

The goal is not to propose clever hacks, but to align automation with Zellij’s actual model.

1. Understanding Zellij’s Mental Model

To use Zellij reliably, it’s essential to understand one core idea:

In Zellij, a session is the shared state.
Terminals are merely clients.

This has several implications:

  • Multiple terminals can attach to the same session
  • All attached terminals are equal peers
  • Zellij does not track “who came first”
  • There is no concept of a primary or owner terminal

This model is deliberate and differs subtly from how many users mentally model terminal multiplexers.


2. The Common Goal — and Why It’s Deceptive

A frequent goal sounds reasonable on the surface:

“Automatically enter a shared work session — but only from the first terminal.”

In practice, users attempt logic like:

  • If the work session exists → create a new one
  • If it does not exist → attach to work

This often relies on checking existing sessions at shell startup.

Unfortunately, this approach is fundamentally flawed.


3. Why list-sessions Cannot Be Used for Control Logic

Zellij session creation is not an atomic operation.

When two terminals start at nearly the same time:

  1. Terminal A checks for existing sessions — none found
  2. Terminal B checks at the same moment — none found
  3. Both attempt to create or attach to the same session

This race condition is unavoidable and cannot be reliably eliminated with shell logic alone.

Important distinction:

  • zellij list-sessions is safe for inspection
  • It is unsafe for coordination

Any logic built on it will eventually fail under concurrency.


4. Why Lock Files Are the Wrong Tool Here

A natural next idea is to use a lock file to ensure only one terminal enters the shared session.

This also fails, for structural reasons:

  • Shell processes are replaced via exec
  • Terminal closure is not a reliable lifecycle event
  • Lock files can easily outlive the process that created them

Without tight coupling to Zellij’s internal lifecycle, such locks become stale state, which is worse than no state at all.


5. The Key Insight: Zellij Has No “First Terminal”

At this point, a crucial realization emerges:

Zellij does not—and intentionally does not—model the concept of a “first client”.

All clients are equal.
All attachments are valid.
The session exists independently of who attached when.

Trying to impose “first terminal” semantics on top of this model creates fragile systems.


6. The Only Stable Design Principle

The investigation leads to a single, reliable principle:

Automatic behavior must be stateless.
Shared state must be explicit.

Translated into practical terms:

Behavior Automatic?
Creating a session Yes
Naming a session Yes
Sharing a session No (explicit only)
Detecting “first terminal” Not possible

Once this principle is accepted, the system becomes predictable.


7. A Minimal, Reliable Fish + Zellij Setup

Below is a minimal Fish shell configuration that avoids all known pitfalls:

1
2
3
4
5
if status is-interactive
if not set -q ZELLIJ_PANE_ID
exec zellij attach --create
end
end

What this guarantees:

  • No nested Zellij instances
  • No race conditions
  • No hidden shared state
  • One session per terminal by default

To collaborate or resume work:

1
zellij attach work

Sharing becomes an intentional action, not a side effect.


8. Why This Works Long-Term

This approach succeeds because it:

  • Aligns with Zellij’s internal model
  • Avoids inferring intent from shell state
  • Requires no external locks or heuristics
  • Degrades gracefully under concurrency

It favors explicit control over clever automation.


9. Conclusion

Zellij is not difficult to automate — but it is easy to automate incorrectly.

Most problems arise from assuming semantics that Zellij does not provide, such as “first terminal” or “implicit ownership”.

Once automation is reduced to stateless defaults and collaboration is made explicit, Zellij becomes stable, predictable, and a pleasure to use.

Sometimes, the most reliable automation is the one that does less.

P.S.

This article is very subjective. If you do not feel comfortable viewing it, please close it as soon as possible.
If you think my article can help you, you can subscribe to this site by using RSS.

Zellij, Fish Shell, and the Illusion of "The First Terminal"

https://iiiyu.com/2026/01/10/Zellij-Fish-Shell-and-the-Illusion-of-The-First-Terminal/

Author

Ewan Xiao

Posted on

January 10th 2026

Updated on

January 9th 2026

Licensed under

Comments