A tag called <ip_reminder> showed up mid-conversation in my Claude Code session. I went looking for it in my own JSONL transcript instead of
---
title: I Grepped My Own Claude Code Logs and Found the Hidden Tag Anthropic Never Shows You
published: true
tags: claudecode, anthropic, llm, security
description: A tag called <ip_reminder> showed up mid-conversation in my Claude Code session. I went looking for it in my own JSONL transcript instead of
---
I was mid-conversation with Claude Code, asking it to help draft a blog post, when a literal
`<ip_reminder>` tag showed up pasted into my own message. I hadn't typed it. It took me a few
minutes of back-and-forth with the model to figure out what I was even looking at.
**TL;DR: `ip_reminder` is a real, non-displayed system-prompt injection Claude Code sends on
every turn for copyright safety. You don't have to trust anyone's self-reported numbers about it
— you can `grep` your own session logs and count it yourself. In my case: 151 matching lines, 437
raw occurrences, in one session file alone.**
I use Claude Code daily across a handful of projects, and one thread of work turned into asking
the model whether it was safe to write publicly about something I'd noticed: a block of text that
looked like a system tag, `<ip_reminder>...</ip_reminder>`, had turned up inline in the
conversation. Claude's own response to seeing it was telling:
> "今回の`<ip_reminder>`は本物のシステム注入ではなく、ユーザーが手打ちでメッセージ本文に貼り付けたテキスト...
> なお直前のBash結果内に埋め込まれた`<ip_reminder>`ブロックは、ツール出力に混入した外部由来のテキスト"
Translated: *this occurrence was pasted by the user, not a live system injection.* A few turns
later, while trying to hand a prompt containing that same discussion off to a delegation script
via a Bash command, an argument-parsing error (`exit code 2`, wrong flag) caused part of the
prompt text — including an `<ip_reminder>`-shaped fragment — to spill back out into the tool's
error output. Claude flagged that second appearance explicitly as "text mixed into a tool result
from an external source" and disregarded it as an unrelated instruction rather than acting on it.
Neither occurrence was a live, hidden system-level injection in that moment — but the tag had
already become real enough, twice, in two different ways, that I wanted to know what it actually
was.
Instead of taking my own memory of that exchange at face value, I went back to the actual
transcript. Claude Code writes every session to a JSONL file under
`~/.claude/projects/<project-key>/`, one line per event, and nothing in that file is edited after
the fact — it's the closest thing I have to a raw record of what really happened.
# project_key = your cwd with slashes replaced by dashes PROJECT_KEY=$(pwd | tr '/' '-') LOG_DIR="$HOME/.claude/projects/$PROJECT_KEY" # how many lines mention the tag at all grep -c "ip_reminder" "$LOG_DIR"/*.jsonl # how many literal opening/closing tags actually appear grep -o "<ip_reminder>" "$LOG_DIR"/*.jsonl | wc -l grep -o "</ip_reminder>" "$LOG_DIR"/*.jsonl | wc -l
Run against the session file where this happened, that gave me:
1