Prompt Linter
Checks a prompt for contradictions, unverifiable wording, dead references and template variables that will never be substituted.
Everything on this page runs in your browser. Nothing is uploaded, logged or sent anywhere — your input is kept in the address bar so a link reopens it, which also means anything you paste travels with the link. Do not share a link to a payload you would not publish.
Every rule here is a structural property of the text — a word that appears, a bracket that does not close, a reference to a step that is not there. None of them is a measured effect on any model's output, and a clean report is not evidence that a prompt is good. It is evidence that these particular mistakes are absent.
- Characters
- 569
- Words
- 82
- Sentences and lines
- 14
- Longest sentence
- 12 words
- Distinct placeholders
- 3
- Must-fix findings
- 2
- Rough token count
- ~143
What the rules found
- check“always” and “never” both appearline 5, column 21
Also at line 3: “Always”. Not a contradiction by itself. It becomes one when the two are about the same thing, and which one wins is not stable across models or across a rewording of the input.
Do not hallucinate. Never make anything up. ^ - checkAsks for JSON and also mentions markdown, a code block or proseline 3, column 63
Also at line 3: “valid JSON”. A fenced code block is not JSON — the fence is three characters your parser has to strip. Decide which one you want and say it once: either the response starts with `{` and contains nothing else, or you strip the fence in code.
…lid JSON. Wrap your answer in a ```json code block. ^ - checkAsks for brevity and for detailline 4, column 26
Also at line 4: “concise”. The model resolves this by picking one, usually the one stated last or most emphatically. Replace both with a number: a word count, a sentence count, a token budget.
Be concise but provide a detailed explanation of your reasoning. ^ - checkUnenforceable: “do not hallucinate”line 5, column 1
There is no point during generation at which the model could check this. It has no oracle for truth and no separate careful mode to switch into. Replace it with something checkable: give it the source text, require quoted spans, and verify the quotes yourself afterwards.
Do not hallucinate. Never make anything … ^
- checkUnenforceable: a prohibition on guessingline 5, column 21
Worth keeping only if you also give the model a way to decline. Pair it with an explicit escape hatch — a `"unknown"` enum value, a null field — or it has nowhere to put the abstention and will guess anyway.
Do not hallucinate. Never make anything up. ^ - noteHedge: “reasonably”line 7, column 46
An instruction with an escape clause is satisfied by ignoring it. If the behaviour is optional, say what decides it; if it is not, drop the hedge.
… professional tone and keep the summary reasonably brief. ^ - noteNot checkable: “concise”line 4, column 4
You cannot write an eval that decides whether an output was this. Whatever you would use to judge it by hand — a length, a reading level, a list of banned constructions — put that in the prompt instead.
Be concise but provide a detailed explanati… ^
- noteNot checkable: “brief”line 7, column 57
You cannot write an eval that decides whether an output was this. Whatever you would use to judge it by hand — a length, a reading level, a list of banned constructions — put that in the prompt instead.
…al tone and keep the summary reasonably brief. ^ - noteNot checkable: “detailed”line 4, column 26
You cannot write an eval that decides whether an output was this. Whatever you would use to judge it by hand — a length, a reading level, a list of banned constructions — put that in the prompt instead.
Be concise but provide a detailed explanation of your reasoning. ^ - noteNot checkable: “professional”line 7, column 7
You cannot write an eval that decides whether an output was this. Whatever you would use to judge it by hand — a length, a reading level, a list of banned constructions — put that in the prompt instead.
Use a professional tone and keep the summary r… ^ - noteNot checkable: “helpful”line 1, column 11
You cannot write an eval that decides whether an output was this. Whatever you would use to judge it by hand — a length, a reading level, a list of banned constructions — put that in the prompt instead.
You are a helpful assistant that extracts structur… ^ - noteA rule that says what not to do and not what to doline 5, column 1
A prohibition names a region of output space and leaves the rest unconstrained. Adding “instead, …” usually costs five words and removes the failure mode entirely.
Do not hallucinate. Never make anything … ^
- noteA rule that says what not to do and not what to doline 5, column 20
A prohibition names a region of output space and leaves the rest unconstrained. Adding “instead, …” usually costs five words and removes the failure mode entirely.
Do not hallucinate. Never make anything up. ^ - must fixTwo spellings of the same placeholder: {customerName} and {customer_name}line 15, column 17
Only one of these gets substituted. The other reaches the model as literal braces, and the model will happily invent a plausible value for it rather than complain.
Known customer: {customer_name} (verified: {customerName… ^ - check{email_body} is interpolated with no delimiter before itline 13, column 1
Whatever goes in here is read by the model as part of the same instruction stream as your rules. A delimiter does not stop injection, but an unbounded interpolation makes it trivial — the pasted text does not even have to escape anything.
{email_body} ^ - check{customer_name} is interpolated with no delimiter before itline 15, column 17
Whatever goes in here is read by the model as part of the same instruction stream as your rules. A delimiter does not stop injection, but an unbounded interpolation makes it trivial — the pasted text does not even have to escape anything.
Known customer: {customer_name} (verified: {customerName… ^ - check{customerName} is interpolated with no delimiter before itline 15, column 44
Whatever goes in here is read by the model as part of the same instruction stream as your rules. A delimiter does not stop injection, but an unbounded interpolation makes it trivial — the pasted text does not even have to escape anything.
…wn customer: {customer_name} (verified: {customerName}) ^ - check1 code fences — an odd number
One of them opens a block that never closes, so everything after it is inside the block as far as any markdown reader is concerned.
- must fixReferences step 4, and there are 0 numbered itemsline 10, column 22
A reference to something that is not in the prompt. The model does not report this — it constructs something to satisfy the reference. Usually the leftover of a rewrite that deleted a list.
Follow the schema in step 4 exactly. ^ - noteThis sentence already appears earlierline 16, column 1
First at line 3. Repetition is sometimes deliberate — a rule restated after a long context block genuinely does get followed more often — but it is more often the residue of two people editing the same prompt.
Always respond with valid JSON. ^
email_body ×1 customer_name ×1 customerName ×1
.!? followed by a space, which mis-splits abbreviations. Contradiction rules fire on the presence of two words anywhere in the prompt and cannot tell whether they are about the same thing, which is why they are reported as "check" and never as "must fix".What a linter can and cannot know
Everything above is decidable from the text alone. An unclosed tag is unclosed; a reference to step 4 in a prompt with three steps is dead; two spellings of the same variable name mean one of them is never substituted. Those are facts, and they are worth catching because each of them fails silently — a model given a broken template does not raise an error, it produces something plausible built on the broken part, and you find out from a customer.
What no linter can know is whether the prompt works. There is no static property of text that predicts output quality, and anything claiming otherwise is selling a score. The rules here are closer to a spell checker than to a test: they remove a category of mistake so that when the output is still wrong, you know it is the instructions and not the plumbing.
The two findings worth acting on first
A placeholder with no delimiter in front of it, and an unenforceable instruction. The first is the entire mechanism of prompt injection: text you did not write, read in the same voice as text you did. The second is load-bearing in a different way — "do not hallucinate" in a system prompt tends to be the only thing standing between the model and a fabricated answer, and it does nothing, so the absence of a real grounding step goes unnoticed for as long as it is there.
Contradictions are reported, not resolved
"Be concise but detailed" is a real instruction people write, and it is not always wrong — sometimes the two words apply to different fields and the prompt is fine. This page cannot tell, so it points at both occurrences and leaves the judgement to you. What it will say is that when they are about the same output, the model picks one, and it usually picks the one nearest the end of the prompt, which means your prompt's behaviour depends on edit order.