RAG
RAG liability: three contract clauses after the German ruling
A German court held Google liable for AI Overview hallucinations. Within seven days we rewrote three contract clauses for every Dutch client whose RAG agent surfaces third-party content.

The morning the ruling landed
It was a Tuesday in early June, just past nine in our Eindhoven office. One of our engineers pinged the team channel with a single line: "We need to look at our RAG agents now." Below it, a Hacker News thread, 527 points and climbing, headline reading that a German court had declared Google liable for false answers in AI Overviews.
Within forty minutes we had pulled the client list and counted: eleven live agents we built for Dutch clients that surface third-party content. Product catalogues, regulatory text, supplier specs, partner knowledge bases. Eleven contracts that did not yet anticipate what the court had just established.
By Friday we had rewritten three clauses across all eleven contracts, sent the redlines to every client, and patched two agent architectures. This is the walkthrough.
What the ruling actually establishes
We will not invent paragraph numbers from a judgment we have not personally read. The coverage that surfaced in the German legal press and on Hacker News converged on one principle: a platform that synthesises an answer from third-party material, and presents that synthesis as fact, can be held responsible for the accuracy of what it presents.
The traditional safe harbour for hosting other people's content, originally Article 14 of the e-Commerce Directive and now folded into the Digital Services Act, was written for a world where a platform stored a user's file or a user's post. It did not anticipate a system that ingests other people's text, rewrites it, and presents the rewrite as the platform's own answer.
If you run a RAG agent, the sentence above is the whole post. Your retriever pulls third-party material. Your generator rewrites it. Your agent presents the rewrite. The act of rewriting moves you from host to publisher.
The moment your agent paraphrases a third-party source instead of quoting it, you have published a new statement of fact. Treat every generated answer as something your company said, not something your sources said.
Why this is not just a Google problem
The instinct in the team channel that Tuesday was to wave it off as a Google-scale issue. A scraped competitor catalogue rephrased by an agent for a Rotterdam wholesaler is not the same scene as Google synthesising the open web for a billion daily users. But the legal principle does not scale with revenue. Whether the agent runs at planet scale or for one regional buyer, the test is the same. Did the system take third-party material, transform it, and present the transformation as an answer.
That test catches almost every interesting RAG architecture we have shipped. The exceptions are agents that only quote verbatim, with citation, and refuse to answer when no source can be cited. We had two of those out of eleven. The other nine paraphrased.
Clause one: the source-of-truth boundary
The first clause we rewrote covers what the agent is contractually permitted to ingest. Before the ruling, our standard language said the agent indexes "client-provided and client-approved source material." That was too loose. A client who pasted three URLs into an onboarding form had, under our old language, approved an entire scraping run.
The new clause splits source material into three classes:
- First-party: the client owns it or has clear written license.
- Third-party permissive: open license, public domain, or written permission on file, with the license text stored alongside the data.
- Third-party restricted: everything else, including scraped content.
The agent is contractually allowed to paraphrase classes one and two. For class three it must quote verbatim, attribute, and link, or refuse to answer. The contract names the responsible party for each class. The client owns class one and two. We do not touch class three without a separate written addendum that names the client as the legally responsible publisher.
Clause two: the freshness window
The second clause was a surprise. We had not written about it before, because staleness had been a quality concern, not a legal one. Now it is both.
If an agent answers a question about a regulation or a price using a chunk retrieved nine months ago, and the chunk is now out of date, the agent has stated a falsehood. Under the German principle, that falsehood belongs to whoever runs the agent. So our new clause specifies a maximum retrieval-to-answer staleness window per data class. For regulatory content it is 30 days. For product specs it is 7 days. For pricing data it is 24 hours. If the freshest available chunk falls outside the window, the agent must say so in the answer.
Most of the architectural change happens in the indexer. The contract change is the part the client signs.
Clause three: the takedown SLA
The third clause covers what happens when a third party tells you that your agent is misrepresenting them. Under the e-Commerce Directive, a host had to act expeditiously upon receiving notice. The DSA tightened that, but the deadline is still a matter of interpretation. For a publisher, courts have historically been less patient.
We wrote a hard 24-hour takedown SLA into every contract. Within 24 hours of a credible written notice, the relevant chunks come out of the index, the agent is patched to refuse answers on the affected topic, and the client is notified. We commit to it. The client agrees that we have authority to act unilaterally if they are unreachable. Saturday and Sunday count.
This is the clause clients pushed back on hardest. Two asked for 72 hours. We held the line at 24. The reason is selfish. We run the agents, so we wear the headline risk if a court reads the SLA as evidence of how seriously the operator takes accuracy.
The architecture patch
Contracts without code are theatre. Two of the eleven agents needed actual changes. The patch below is the gate we dropped in front of every generator call. Plug your existing paraphrase and quote-only generators in as callables and the rest of the file runs as-is on Python 3.11+.
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Callable, Literal
LicenseClass = Literal["first_party", "third_permissive", "third_restricted"]
DataClass = Literal["regulatory", "product_spec", "pricing", "other"]
@dataclass
class Chunk:
text: str
source_id: str
license_class: LicenseClass
data_class: DataClass
retrieved_at: datetime
source_url: str | None = None
@dataclass
class Answer:
text: str
mode: Literal["paraphrase", "quote", "refuse"]
@classmethod
def refuse(cls, reason: str) -> "Answer":
return cls(text=reason, mode="refuse")
WINDOWS: dict[DataClass, timedelta] = {
"regulatory": timedelta(days=30),
"product_spec": timedelta(days=7),
"pricing": timedelta(hours=24),
"other": timedelta(days=30),
}
def is_stale(chunk: Chunk, now: datetime) -> bool:
return (now - chunk.retrieved_at) > WINDOWS[chunk.data_class]
def answer(
query: str,
chunks: list[Chunk],
now: datetime,
paraphrase: Callable[[str, list[Chunk]], str],
quote_only: Callable[[str, list[Chunk]], str],
) -> Answer:
# Hard fail at the boundary if any chunk lacks provenance.
for c in chunks:
assert c.source_id, "chunk has no source_id"
assert c.license_class, "chunk has no license_class"
usable = [c for c in chunks if not is_stale(c, now)]
if not usable:
return Answer.refuse("No fresh source for this question.")
restricted = any(c.license_class == "third_restricted" for c in usable)
if restricted:
return Answer(text=quote_only(query, usable), mode="quote")
return Answer(text=paraphrase(query, usable), mode="paraphrase")
if __name__ == "__main__":
sample = [
Chunk(
text="Widget X ships in 5 days.",
source_id="catalogue-row-9821",
license_class="first_party",
data_class="product_spec",
retrieved_at=datetime.utcnow() - timedelta(days=2),
)
]
out = answer(
"How fast does Widget X ship?",
sample,
datetime.utcnow(),
paraphrase=lambda q, cs: cs[0].text,
quote_only=lambda q, cs: f'"{cs[0].text}"',
)
print(out)
The most important change is not in the generator. It is the assertion at the boundary. Half the indexers we audited had chunks with empty source_id fields, left over from early prototyping. Those chunks would, under the new contracts, expose the client. We deleted them and reindexed.
The five-minute call to every client
Every Dutch client got the same five-minute call this week. The substance was unchanged across calls, so we wrote it down once.
The legal ground under AI agents that surface other people's content moved this week. The agent we built for you is fine, but the contract underneath it was written before the ground moved. We are sending you redlines tonight. Please sign within fourteen days. If you have a third-party source we did not catch, tell us by Friday.
ABN client call script, June 2026
No client refused. Two asked to defer the takedown SLA until their compliance team reviewed it. One asked whether their old PDF help centre, which we had indexed, counted as first-party. It did, after we verified they had written permission from the contractor who drafted it eight years ago.
The audit you can run before lunch
If you run a RAG agent and you cannot phone your lawyer this afternoon, you can still close the largest gap in five minutes. Open the database that backs your vector store. Run one query: how many chunks have a non-empty source identifier, and how many do not. Then run the second query: of the chunks with a source, how many have a recorded license class.
If those two counts do not equal your total chunk count, you have exposure. Stop the agent from paraphrasing the unclassified chunks. Force it to quote verbatim with citation, or refuse. That single change moves you back inside the host-side reading of the safe harbour for the affected content while your lawyer catches up.
When we built the supplier-knowledge RAG agent for a Rotterdam wholesaler last year, the thing we ran into was that their product database mixed first-party catalogue text with scraped supplier specs in the same table. We ended up solving it by tagging each row with a license class at ingest time, then refusing to start the agent if any row lacked a class. The same pattern is now table stakes for any AI agent that touches content the client does not own.
Pull up your vector store. Count the chunks without a source. That number is your homework.
Key takeaway
The moment your RAG agent paraphrases third-party content, you stop being a host and start being a publisher. Rewrite your contracts to match.
FAQ
Does the German ruling bind Dutch RAG operators?
It does not bind Dutch courts directly, but the underlying principle of publisher liability for synthesised answers is consistent across EU law. Treat it as a strong signal of where Dutch courts will land.
Do we have to stop using RAG agents entirely?
No. Agents that pass the new bar either quote verbatim with citation, or paraphrase only content the client owns or has licensed. Most production RAG can be brought into shape with an index audit and a contract redline.
What if our agent only ever quotes verbatim?
Then you stay closer to the host safe harbour. You still need accurate citation, a working takedown path, and proof the quote was not transformed. Verbatim mode is not a free pass, but it is a much safer default.
How fast should the takedown SLA be?
We commit to 24 hours including weekends. Longer windows are defensible but harder to argue in front of a court that has just read a one-week-old hallucination still live in your index.