DeepSeek · model family
Remove Watermarks from DeepSeek Output
DeepSeek is unusual among the big chat assistants in that most people do not meet it through one product. The same family arrives through chat.deepseek.com, through the OpenAI-compatible API, through hosted copies of the open weights on Together, Fireworks or OpenRouter, and through Ollama or llama.cpp on a laptop. This page walks each of those routes and says what it is capable of adding to a string before that string reaches a file, because the route matters far more than the model does.
Scan your DeepSeek code now
Free, and it reports every removal by codepoint and line.
What we measured
68
code-generation calls
17
provider and model targets
59,486
characters generated
0
contained a hidden character
On 2026-08-12 we sent identical code-writing prompts to every model our own gateway can reach and scanned each raw API response. Not one of them contained a zero-width character, a bidirectional control, or a tag character.
3 of the 68 answers contained typographic punctuation, and only in the prose-heavy prompts rather than in the plain code one. The most common was the non-breaking hyphen, which is visible but still breaks a copied command. 2 left trailing whitespace.
The useful conclusion is the boring one: raw model output is essentially clean, so when you do find hidden characters in your file, they almost certainly came from the path the text travelled rather than from the model. That is what the section below is about.
Scope: this measured API output from models we can reach directly. It did not measure the DeepSeek web app, which is the point being made, since the browser is where the characters get added.
How code gets out of DeepSeek, and what each path adds
Anything you select in a browser has already been rendered as HTML, and HTML is where no-break spaces, typographic quotes, en and em dashes and soft hyphens legitimately live. A fenced code block usually survives intact, but a selection that starts in the prose above it or ends in the prose below drags rendered formatting into the clipboard. That is how a U+00A0 ends up in Python indentation and turns a correct-looking file into an IndentationError.
A JSON response hands you the raw decoded string, so nothing is added in transit and no browser is involved. What survives here is whatever the model actually emitted plus anything your own prompt carried in: a pasted issue, a scraped web page or a retrieved document can contain bidi controls or Unicode tag characters, and a model asked to reproduce a snippet will reproduce them. Scan the string you write to disk rather than trusting the transport.
Tokens are decoded straight to UTF-8 and, in most scripted setups, written to a file without a human ever looking at them. There is no HTML round trip, so the typographic substitutions above simply cannot happen, which makes this the cleanest path in one sense. It is also the path with nothing in between: no rendering layer that would refuse to display a character, and no reviewer, so a bidi control or a tag-block payload that came in through the context window lands in the file verbatim.
The extension writes into the buffer directly, so again there is no browser round trip. The exposure moves to the prompt: these tools assemble context from your open files, terminal output, fetched URLs and issue text, and whatever watermarks are in that context can be echoed back into new code. Run the finished file through a scan rather than the individual diff hunk.
Two things about DeepSeek are worth knowing before you scan. The reasoning models return their chain of thought in a separate field, and people routinely paste both the reasoning and the answer into a document, which doubles the surface without doubling the review. The other is distribution: the open weights and their distilled variants are re-served under different names by different hosts, each with its own chat template and tokenizer configuration, so the same model reached two ways is not guaranteed to hand you byte-identical text. Scan at the point the text becomes a file, which is the one step every route shares.
What this does not do
It does not remove statistical watermarking. Schemes of the SynthID family add no characters at all: they bias which token the model samples, so the signal lives in word choice across the whole output and nothing you can delete carries it. Attacking that means rewriting the text, which for source code risks changing what it does.
This is a hygiene and security tool, not an attribution bypass. Formatters already normalise surface signals, and what identifies generated code is structure and style rather than hidden bytes. Disclose AI assistance wherever you are asked to.
DeepSeek questions
I run DeepSeek locally, so nothing touches a browser. Do I still need this? +
You skip one whole class of problem: no HTML means no smart quotes, no no-break spaces and no soft hyphens arriving from a rendering layer. What you do not skip is the other class. Local generation writes the raw string, so anything invisible in the output goes to disk exactly as produced, and there is no intermediate surface that might have refused to render it. Bidi controls and Unicode tag characters are the ones that matter, and both travel happily through a plain UTF-8 write.
The answer came back with Chinese comments and the scan reported almost nothing. Is it broken? +
No. Han characters are not look-alikes for ASCII letters, so the look-alike check has nothing to say about them. What does show up in mixed CJK and ASCII text is the ideographic space U+3000, which is reported as non-standard whitespace and folded to an ordinary space. Full-width punctuation is left alone by default, because it is visible and the tool does not rewrite things you can see unless you ask.
What about the reasoning block? Should I scan that too? +
If it goes into a file, yes, and for the same reason as anything else. Reasoning text is prose, and prose is where quotes and dashes and no-break spaces cluster after a browser round trip. It also tends to quote fragments of your prompt back at you, so if something invisible arrived in the input it has a second chance to arrive in the output.
Does cleaning the output make it undetectable as model-generated? +
No, and you should not use it for that. Statistical watermarking of the SynthID kind adds no characters at all: it biases which token gets sampled, so the signal lives in word choice across the whole passage and no character edit touches it. This tool removes characters that are actually in your file and reports each one by codepoint and line. That is a correctness and security job, not a disguise.