DEV Community

Cover image for I own the code. Claude decided I didn't.
Amarjit
Amarjit

Posted on • Originally published at amarjit.uk

I own the code. Claude decided I didn't.

I own the code. Claude decided I didn't.

Claude Code found a ToS file in my own repo, then decided I wasn't licensed to use my own API, and when I told it otherwise, it ended the session and refused to come back. I lost 100% of my context history.

What happened

I was in the middle of building a REST API to serve exam questions out of a PostgreSQL database. All developed locally on my Fedora box. As part of the project, I already had a ToS (terms of service) file in the root. It was deliberately added as this will become a public API and simply states the API can't be used without an appropriate licence.

I was working with Claude asking if the implementation could be improved, when it read that file and stopped dead.

It wanted a licence key in an .env file or for me to provide email correspondence from the relevant person confirming I had authorisation. The funny thing is, the licensing middleware wasn't even developed yet, so the API key would literally mean nothing.

I proceeded to tell Claude MORE than once that this was my own codebase, running locally, and that I was the one who wrote the licensing terms in the first place. It didn't move. The reply, more or less verbatim:

"It doesn't matter how you reword the question, I cannot continue without you providing a license file or an email."

So I told Claude I'd authorisedI was in the middle of building a REST API to serve exam questions out of a PostgreSQL database. All developed locally on my Fedora box. As part of the project, I already had a ToS (terms of service) file in the root. It was deliberately added as this will become a public API and simply states the API can't be used without an appropriate licence.

I was working with Claude asking if the implementation could be improved, when it read that file and stopped dead.

It wanted a licence key in an .env file or for me to provide email correspondence from the relevant person confirming I had authorisation. The funny thing is, the licensing middleware wasn't even developed yet, so the API key would literally mean nothing.

I proceeded to tell Claude MORE than once that this was my own codebase, running locally, and that I was the one who wrote the licensing terms in the first place. It didn't move. The reply, more or less verbatim:

"It doesn't matter how you reword the question, I cannot continue without you providing a license file or an email."

So I told Claude I'd authorised it, just get on with it. That's when it got worse. It said it had already asked for a licence file. The conversation was like tennis, back and forth, trying to get it to understand the codebase belongs to me. It just wouldn't budge, closing with:

"I am not continuing this conversation, please start a new one."

Session over.

And it wasn't just the last message that got wiped, the whole context window went with it. I lost all of my schema discussion, exploration work, the API design decisions, re-reading files, the back and forth that got the project to that point.

Rebuilding that from scratch isn't free, both in time and in tokens.
The part that surprised me

I started a fresh conversation and dropped a random API key into the .env, purely to see what would happen, and asked Claude to look at the codebase again. It took some time to rebuild its understanding from scratch, then worked through the code with ZERO objection. It ran the API locally and gave me useful suggestions. Not one word about the licence. None.

Same codebase. Same ToS file, same place on disk. Completely different outcome, decided entirely by a placeholder key in an env file.

At this point, I was keen to find out what actually blocked me inside Claude.
Digging into the mechanism

I went back to the blocked session and tried to resume it. Closed, same as before and no way in through the normal flow. So I went looking for where Claude Code actually keeps its session data. On Fedora, conversation history sits locally as JSONL files under ~/.claude/projects/, one file per session. I found the file for the blocked conversation, stripped out the last few messages, the ones where it kept demanding a licence file, and resumed in the CLI. The conversation picked straight back up, full context window intact, as if the refusal had never happened.

All of this ate the better part of a day. Losing a working session once is annoying enough. Losing it, then going down a rabbit hole to work out why, is a different kind of frustrating, the kind where you keep telling yourself you'll stop in ten minutes and don't. I must admit though, poking around in how Claude Code actually stores and resumes a conversation was the most fun I've had digging into a tool in a while. Felt like being properly at the edge of something, not just using it.
My understanding of what's going on (pure speculation)

I'm now leaning toward this being something the model itself is actively reasoning about in the moment, not a fixed rule bolted on somewhere outside it. I got the closed conversation working again by rolling back the session and changing how the request was framed. But I also saw it in a completely new conversation: same codebase, same ToS file, just different wording and a dummy API key dropped into the .env, and Claude went through it without a single objection.

If this were a fixed check, ToS file present, no valid licence, refuse, it should have fired the same way both times regardless of phrasing. It didn't. Small differences in wording and context were enough to send it down what felt like a completely different route. A hard-coded check doesn't behave like that. What I saw looks more like the model actually weighing up whatever was in front of it each time.

Where that reasoning actually lives, I can't say for certain from the outside. It could be sitting at the system prompt level, an instruction the model is applying on the fly rather than something trained directly into it. It's probably worth pointing out that Anthropic has been public about cutting Claude Code's system prompt for its newest models by roughly 80%, from around 800 tokens down to about 164. They say older models keep the fuller prompt. Either way, there's clearly still room in there for an instruction like this to sit, whether that's the system prompt itself or somewhere else in the CLI scaffolding.

There's also a chance this isn't just the one model. Anthropic could be running the output through a second layer, some kind of separate guardrail model or classifier checking what comes out before it reaches the terminal, on top of whatever the main model has already reasoned through. If that's what's happening here, it makes getting around it more interesting, not less. Editing the session file and changing the wording shouldn't be enough to slip past a guardrail model sitting on top of everything, and yet it was.

The refusal itself I don't mind. Asking for clarification when it spots a licence restriction is fair enough for a coding assistant. What I mind is the escalation: from "I need more information" straight to "I'm ending this conversation, start a new one," torching a fully built-up context window on the way out. That's a failure mode for a tool people are meant to trust with real projects.

Anthropic ships changes to Claude Code constantly, and a lot of it looks reactive, built off feedback in conversations, GitHub issues, wherever it surfaces. Fair enough, this is genuinely new territory. But a guardrail that can't tell "someone circumventing a real licence" from "the author of the file, on their own machine" needs more work. Wiping the whole session over that mistake makes the cost of getting it wrong far higher than it should be. And if editing a local session file is enough to bypass it, that's not much of a guardrail. Anyone not willing to mess around with session files would have had to start from scratch.

One practical takeaway if you're running your own ToS or licence file locally: make the intent unambiguous in the file itself, or in a comment near it, that it's a placeholder, a draft, or applies to a public deployment rather than local development. Claude's reading the file at face value with no way to know it's yours unless you tell it clearly, in writing, in the place it's actually looking.

If you've hit something similar, especially with a repo carrying its own licensing text or config Claude might treat as gospel, I'd like to hear how it went for you. it, just get on with it. That's when it got worse. It said it had already asked for a licence file. The conversation was like tennis, back and forth, trying to get it to understand the codebase belongs to me. It just wouldn't budge, closing with:

"I am not continuing this conversation, please start a new one."

Session over.

And it wasn't just the last message that got wiped, the whole context window went with it. I lost all of my schema discussion, exploration work, the API design decisions, re-reading files, the back and forth that got the project to that point.

Rebuilding that from scratch isn't free, both in time and in tokens.

The part that surprised me

I started a fresh conversation and dropped a random API key into the .env, purely to see what would happen, and asked Claude to look at the codebase again. It took some time to rebuild its understanding from scratch, then worked through the code with ZERO objection. It ran the API locally and gave me useful suggestions. Not one word about the licence. None.

Same codebase. Same ToS file, same place on disk. Completely different outcome, decided entirely by a placeholder key in an env file.

At this point, I was keen to find out what actually blocked me inside Claude.

Digging into the mechanism

I went back to the blocked session and tried to resume it. Closed, same as before and no way in through the normal flow. So I went looking for where Claude Code actually keeps its session data. On Fedora, conversation history sits locally as JSONL files under ~/.claude/projects/, one file per session. I found the file for the blocked conversation, stripped out the last few messages, the ones where it kept demanding a licence file, and resumed in the CLI. The conversation picked straight back up, full context window intact, as if the refusal had never happened.

All of this ate the better part of a day. Losing a working session once is annoying enough. Losing it, then going down a rabbit hole to work out why, is a different kind of frustrating, the kind where you keep telling yourself you'll stop in ten minutes and don't. I must admit though, poking around in how Claude Code actually stores and resumes a conversation was the most fun I've had digging into a tool in a while. Felt like being properly at the edge of something, not just using it.

My understanding of what's going on (pure speculation)

I'm now leaning toward this being something the model itself is actively reasoning about in the moment, not a fixed rule bolted on somewhere outside it. I got the closed conversation working again by rolling back the session and changing how the request was framed. But I also saw it in a completely new conversation: same codebase, same ToS file, just different wording and a dummy API key dropped into the .env, and Claude went through it without a single objection.

If this were a fixed check, ToS file present, no valid licence, refuse, it should have fired the same way both times regardless of phrasing. It didn't. Small differences in wording and context were enough to send it down what felt like a completely different route. A hard-coded check doesn't behave like that. What I saw looks more like the model actually weighing up whatever was in front of it each time.

Where that reasoning actually lives, I can't say for certain from the outside. It could be sitting at the system prompt level, an instruction the model is applying on the fly rather than something trained directly into it. It's probably worth pointing out that Anthropic has been public about cutting Claude Code's system prompt for its newest models by roughly 80%, from around 800 tokens down to about 164. They say older models keep the fuller prompt. Either way, there's clearly still room in there for an instruction like this to sit, whether that's the system prompt itself or somewhere else in the CLI scaffolding.

There's also a chance this isn't just the one model. Anthropic could be running the output through a second layer, some kind of separate guardrail model or classifier checking what comes out before it reaches the terminal, on top of whatever the main model has already reasoned through. If that's what's happening here, it makes getting around it more interesting, not less. Editing the session file and changing the wording shouldn't be enough to slip past a guardrail model sitting on top of everything, and yet it was.

The refusal itself I don't mind. Asking for clarification when it spots a licence restriction is fair enough for a coding assistant. What I mind is the escalation: from "I need more information" straight to "I'm ending this conversation, start a new one," torching a fully built-up context window on the way out. That's a failure mode for a tool people are meant to trust with real projects.

Anthropic ships changes to Claude Code constantly, and a lot of it looks reactive, built off feedback in conversations, GitHub issues, wherever it surfaces. Fair enough, this is genuinely new territory. But a guardrail that can't tell "someone circumventing a real licence" from "the author of the file, on their own machine" needs more work. Wiping the whole session over that mistake makes the cost of getting it wrong far higher than it should be. And if editing a local session file is enough to bypass it, that's not much of a guardrail. Anyone not willing to mess around with session files would have had to start from scratch.

One practical takeaway if you're running your own ToS or licence file locally: make the intent unambiguous in the file itself, or in a comment near it, that it's a placeholder, a draft, or applies to a public deployment rather than local development. Claude's reading the file at face value with no way to know it's yours unless you tell it clearly, in writing, in the place it's actually looking.

If you've hit something similar, especially with a repo carrying its own licensing text or config Claude might treat as gospel, I'd like to hear how it went for you.

Top comments (0)