How to delete a secret from fish shell history on Mac

Use fish’s own history delete, not sed. It rewrites the file for you and saves right away. Then check the other places fish keeps values in plain text.

You typed set -x GITHUB_TOKEN ghp_… or pasted a curl -H "Authorization: Bearer sk-…" into fish, and now the full token sits in your history, one up-arrow away and in plain text on disk. fish does not use .zsh_history or HISTFILE, so the zsh and bash recipes do not apply. Its history lives in its own file, in its own format, and fish has a built-in command to delete entries from it.

First, rotate the key if it went anywhere else. Deleting it from history protects the copy on your Mac. If the command also reached a shared terminal recording, a screen share or a log, revoke the key at the provider: see what to do when you leak an API key.

1. Find where fish keeps history

By default, fish writes history to ~/.local/share/fish/fish_history (or $XDG_DATA_HOME/fish/fish_history if you set that variable). Each entry is a small YAML-like block:

- cmd: set -x GITHUB_TOKEN ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  when: 1791173800

If you set fish_history to another session name, the file is ~/.local/share/fish/NAME_history instead. ls ~/.local/share/fish/ shows which ones exist.

2. Find the entry without typing the secret again

Search by something that identifies the command, such as the variable name or the token prefix (ghp_, AKIA, sk-, xoxb-), not by the full value. Whatever you type is itself saved as a new history entry, so searching for the whole token creates another copy. Starting the line with a space keeps it out of history (see step 6).

 history search --contains GITHUB_TOKEN

Or look at the file directly, with line numbers:

 grep -n 'ghp_' ~/.local/share/fish/fish_history

3. Delete it with history delete

Run history delete with a search string. With no other flag it matches entries that contain the string, lists them and asks which ones to remove:

 history delete --contains GITHUB_TOKEN
[1] set -x GITHUB_TOKEN ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Enter nothing to cancel the delete, or
Enter one or more of the entry IDs or ranges like '5..12', separated by a space.
For example '7 10..15 35 788..812'.
Enter 'all' to delete all the matching entries.

Type an ID such as 1, a range, or all, and press Return. fish removes the entries and saves the history file immediately; you do not need to run history save. Pressing Return on its own cancels.

To delete one exact command without the prompt, for example in a script, pass the whole command line. The builtin needs both flags; --exact on its own fails with builtin history delete --exact requires --case-sensitive:

 history delete --exact --case-sensitive 'set -x GITHUB_TOKEN ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Then confirm it is gone from the file:

 grep -c 'ghp_' ~/.local/share/fish/fish_history

Why not sed? You can edit the file by hand, but each entry spans several lines (- cmd:, when:, sometimes paths:), so a one-line sed '/ghp_/d' leaves the rest of the entry behind, and you have to remember that BSD sed on macOS needs -i ''. history delete rewrites the file in fish’s own format. In our test on fish 4.0, another fish session that saved its own history after the delete did not write the deleted entry back. Windows that were already open can still offer it on up-arrow from memory, so close them once you are done.

4. Check fish_variables for set -U

If you ever stored the key as a universal variable, with set -U or set -Ux, fish saved the value in plain text in ~/.config/fish/fish_variables, as a line like SETUVAR GITHUB_TOKEN:ghp_…. It survives restarts and is copied by anything that syncs or backs up your dotfiles. Check for it and erase it the fish way:

 grep -n 'GITHUB_TOKEN' ~/.config/fish/fish_variables
set -Ue GITHUB_TOKEN

Also look for a hard-coded set -gx line in ~/.config/fish/config.fish or ~/.config/fish/conf.d/, especially if your fish config lives in a public dotfiles repository.

5. Backups still have the old file

Time Machine and any dotfile sync keep earlier copies of fish_history. Deleting the entry does not reach those. That is the main reason rotating a real key beats cleaning up after it.

6. Keep the next one out of history