Skip to content

Instantly share code, notes, and snippets.

@irazasyed
Created May 12, 2026 19:47
Show Gist options
  • Select an option

  • Save irazasyed/b58255174724b39eb4b757b9602bfc52 to your computer and use it in GitHub Desktop.

Select an option

Save irazasyed/b58255174724b39eb4b757b9602bfc52 to your computer and use it in GitHub Desktop.
Claude Code suspends on launch with `suspended (tty input)` in one specific directory

Claude Code suspends on launch with suspended (tty input) in one specific directory

Symptom

Running claude in one specific project directory immediately returns the shell prompt with:

[1]  + 53014 suspended (tty input)  claude
  • Happens only in that directory; works fine elsewhere.
  • claude --version and claude --help work normally — the issue is only during interactive startup.
  • Typing fg resumes claude and it runs normally:
    ✦ ➜ fg
    [1]  + 5551 continued  claude
    

What suspended (tty input) actually means

A process receives SIGTTIN when it tries to read from the controlling terminal while not in the terminal's foreground process group. The shell catches that and prints suspended (tty input). So claude is starting up, somehow losing the TTY foreground group, then attempting to read input → suspended.

This is not a tty-mode issue (stty tostop is not involved — that's tty output).

Root cause

Stale per-project state in ~/.claude.json. Claude Code keeps a projects map in that file, keyed by absolute path. The entry for the directory contained corrupt/stale values from a prior session (in my case, lastFpsAverage: 0.65, an uninstall first-prompt, and other leftovers). Reading that entry on launch put claude into a state where it lost TTY foreground during UI bring-up.

Files that are not the cause and can be skipped during diagnosis:

  • .mcp.json
  • CLAUDE.md
  • .claude/settings.local.json
  • .claude/skills/
  • Shell rc files
  • Terminal emulator
  • Project source / git state

I confirmed this by copying .claude/ and CLAUDE.md into /tmp/test/ — claude started fine. The trigger lives in ~/.claude.json, not the project.

Fix

Quit claude in all terminals first (it rewrites ~/.claude.json on exit and would clobber the edit).

Back up, then delete the offending project entry:

cp ~/.claude.json ~/.claude.json.bak

python3 -c "
import json
with open('/Users/USERNAME/.claude.json') as f: d = json.load(f)
removed = d['projects'].pop('/absolute/path/to/project', None)
with open('/Users/USERNAME/.claude.json','w') as f: json.dump(d, f, indent=2)
print('Removed:', removed is not None)
"

Replace /Users/USERNAME/.claude.json and /absolute/path/to/project with real paths.

Launch claude in the directory again. Trust dialog and onboarding re-run once, then it's fine.

Diagnosing whether your problem is the same

Before touching anything, confirm the pattern:

  1. Directory-specific? cd ~ && claude — works? → yes, directory-specific.
  2. Interactive-only? claude --version returns normally? → yes, interactive startup is the trigger.
  3. fg recovers it? After the suspend, type fg — claude resumes cleanly? → yes, it's a TTY foreground loss, not a crash.
  4. Project config innocent? Copy .claude/ and CLAUDE.md to /tmp/foo and run claude there — works fine? → project files are not the cause.

If all four match, the stale ~/.claude.json entry is almost certainly it.

Other suspects worth ruling out (if the above doesn't match)

If your symptom diverges, here's the broader diagnostic tree:

  • .mcp.json MCP servers — rename to .mcp.json.off, retry. An MCP server stealing the TTY foreground group via tcsetpgrp() would do this.
  • Shell wrapper / aliastype claude should show /path/to/claude, not a function or alias.
  • stty tostopstty -a | grep tostop. If tostop is set, stty -tostop.
  • Lost job controlsetopt | grep monitor (zsh) should show monitor. If not, setopt MONITOR.
  • Inherited bad process group — open a fresh terminal window before testing.
  • Capture startup outputscript /tmp/claude.log claude, then inspect /tmp/claude.log for anything claude printed before suspending.

Prevention

No real prevention — this is a Claude Code bug (corrupt project state causing interactive-startup TTY mishandling). The state file isn't user-facing, so it can drift between versions or after crashes.

If you hit it again in another directory:

  • Same recipe: pop that project's entry from ~/.claude.json.
  • Keep ~/.claude.json.bak around in case you ever need to recover the old state.

TL;DR

# claude is suspended on launch in one specific dir → ~/.claude.json has bad state for it

cp ~/.claude.json ~/.claude.json.bak
python3 -c "
import json
p = '/Users/USERNAME/.claude.json'
target = '/absolute/path/to/project'
with open(p) as f: d = json.load(f)
d['projects'].pop(target, None)
with open(p,'w') as f: json.dump(d, f, indent=2)
"
# launch claude again in the dir — trust prompt + onboarding once, then fine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment