Most risk frameworks group the ways to treat a risk into four: reduce it, share it, accept it, or avoid it. The first three get almost all of the engineering attention in AI programs, because they let the project ship. Avoidance, deciding not to start or not to continue the activity that creates the risk, is usually treated as a failure of imagination. That is a mistake for LLM systems in particular. Model behaviour cannot be proven, only sampled, so a control that depends on the model behaving well is weaker than it looks, and the only control that holds with certainty is the absence of the capability.
This article treats avoidance as an engineering tool rather than a veto. It covers where avoidance sits among the treatments, the criteria that make it the right answer, how to apply it at the level of a single feature instead of a whole project, a worked example with the scoring arithmetic, and how to make a 'no' stick: a decision that is not enforced in code decays within a few release cycles.
Where avoidance sits among the treatments
Among its treatment options, ISO 31000 includes avoiding the risk by deciding not to start or continue with the activity that gives rise to it. The NIST AI Risk Management Framework builds the same idea into its MANAGE function in two places. MANAGE 1.1 asks for a determination of whether the AI system achieves its intended purpose and whether its development or deployment should proceed at all. MANAGE 1.3 asks that responses to high-priority risks be developed, planned and documented, and names the options as mitigating, transferring, avoiding, or accepting. Avoidance is therefore not an exceptional escalation; it is a normal output of the process, and a program that never produces one is probably not assessing honestly.
Some avoidance is not a choice. The EU AI Act's Article 5 prohibits a set of practices outright, among them social scoring, untargeted scraping of facial images to build facial recognition databases, and emotion recognition in workplaces and schools outside medical or safety uses. For anything on that list the decision is already made, and the work is detection: making sure a product team does not drift into a prohibited practice by adding one feature to an otherwise lawful system.
The decision flow below shows the ordinary path. Avoidance is reached only after mitigation and transfer have been considered and found insufficient, and it has two forms: declining the whole use case, or cutting the single capability that carries the risk while keeping the rest.
When avoidance is the right answer
Four conditions, any one of which is sufficient, make avoidance the right treatment. First, residual risk after every feasible control is still above the stated tolerance; 'feasible' includes cost, because a control that costs more than the feature earns will be quietly switched off later. Second, the harm is severe and irreversible and there is no detective control fast enough to stop it: an agent that can wire money, delete production data, or send messages to thousands of customers needs prevention, not monitoring. Third, the only available mitigation is an instruction to the model. A system prompt that says 'never reveal other customers' data' is a request, not a control, and an assessment that relies on it should score the residual as if the instruction were absent. Fourth, the activity is prohibited, or will be before the system is retired.
The second and third conditions matter most for LLM products. Prompt injection, for example, has no complete fix today: any text the model reads can steer it. A system that combines untrusted input, access to private data, and a channel to send data out has a risk whose likelihood cannot be driven to near zero by filtering. The reliable treatment is to avoid one of the three ingredients, which is exactly a scope cut.
| Situation | Why mitigation falls short | Avoidance option |
|---|---|---|
| Agent reads inbound email and can send email | injected text can trigger exfiltration | remove send; draft only |
| Chatbot open to anonymous users, including minors | age assurance is probabilistic | exclude the audience or the topic |
| Retrieval over HR records for a general assistant | access checks in the prompt are not enforced | remove the data class from the index |
| Model executes generated code on shared hosts | sandbox escape is high impact | no execution; return code to a human |
| Automated decisions on credit or hiring | explanation and appeal duties are heavy | advisory output only |
Avoidance at feature granularity
Whole-project declines are rare and politically expensive. Scope cuts are cheap and precise: the project ships, minus the one capability that carried the unacceptable risk. The useful habit is to decompose each high-scoring register entry into the capabilities that produce it and ask, for each, what the product loses without it. The common cuts are: removing a tool; downgrading a tool from write to read; removing a data class from retrieval; removing an audience, a language or a jurisdiction; removing autonomy by putting a human approval step before an action; and removing an output channel such as rendering links or images that can carry data out.
A scope cut is avoidance, not mitigation, when it removes the capability rather than guarding it. A refund tool with a dollar limit is mitigation: the risk exists, smaller. No refund tool at all is avoidance: the risk of the agent issuing a fraudulent refund is gone, and the residual moves to a different, well-understood process. The distinction matters for scoring, because a removed capability needs no monitoring for that risk, only for its reappearance.
Worked example: a support agent with a refund tool
Consider a support agent for an online retailer. The proposal gives it order lookup, address change and refund tools, and it reads customer messages that may contain injected text. The register scores likelihood and impact on 1 to 5 scales, the appetite threshold for customer-facing financial risk is 8, and the team has assessed three controls for the refund risk: a per-refund limit of $50, a daily cap per account, and an anomaly alert reviewed the next morning. The script below is the arithmetic the council saw.
APPETITE = 8 # max residual score for this risk category
risks = {
"fraudulent refund via injection": dict(likelihood=4, impact=4,
controls={"per-refund limit": (0, 1), "daily cap": (1, 0), "next-day alert": (0, 0)}),
"address change hijack": dict(likelihood=3, impact=4,
controls={"step-up verification": (2, 0)}),
"wrong order status": dict(likelihood=3, impact=1, controls={}),
}
def residual(r):
l, i = r["likelihood"], r["impact"]
for dl, di in r["controls"].values(): # each control lowers likelihood or impact
l, i = max(1, l - dl), max(1, i - di)
return l * i
for name, r in risks.items():
score = residual(r)
verdict = "within appetite" if score <= APPETITE else "AVOID or redesign"
print(f"{name:34s} inherent={r['likelihood'] * r['impact']:2d} residual={score:2d} {verdict}")The output shows the refund risk at an inherent 16 and a residual of 9: the limit cuts impact from 4 to 3 and the cap cuts likelihood from 4 to 3, while the next-day alert is detective and too slow to change either number. The address change risk falls from 12 to 4 with step-up verification, and order status sits at 3. Only one entry is out of appetite, and it maps to exactly one capability.
The council's options were to find another control, accept the risk above appetite with an executive sign-off, or remove the refund tool. A stronger control existed, human approval of each refund, but it removes the reason for automating refunds in the first place, so it is avoidance in practice and the honest record names it as such. The decision was to ship lookup and address change, cut refunds, and have the agent open a ticket in the existing refund queue with the order details pre-filled. Customers lost a few minutes, the business lost no money to injected refund requests, and the risk register entry closed with a residual of zero for that path plus a new, small entry for ticket spam.
Making the decision stick
An avoidance decision that lives only in meeting minutes erodes in predictable ways: a new engineer adds the tool back because the ticket asked for it, a platform team grants a broader API scope for an unrelated reason, a vendor ships the capability as a default, or a prompt change starts producing the forbidden output. Treat the decision as a control with an ID and enforce it in as many layers as the capability can enter through.
The first layer is a machine-readable avoidance register. The second is a CI check that every agent manifest is compared against it, so the reintroduction fails a build rather than an audit. The third is the tool registry or identity system: the service account behind the agent cannot be granted the refund API scope at all. The fourth is the network: egress rules or the model gateway deny the endpoint. The last is a detector over traces that alerts if anything resembling the avoided action appears, which catches the paths nobody predicted.
# avoidance_register.yaml (excerpt)
# - id: AVD-017
# applies_to: [support-agent]
# forbid_tools: [issue_refund, create_credit_note]
# forbid_scopes: [payments.refunds.write]
# decided: 2026-09-14
# review_by: 2027-03-14
import sys, yaml, datetime
def check(manifest_path, register_path):
manifest = yaml.safe_load(open(manifest_path))
register = yaml.safe_load(open(register_path))
failures = []
for d in register:
if manifest["name"] not in d["applies_to"]:
continue
for t in set(manifest.get("tools", [])) & set(d.get("forbid_tools", [])):
failures.append(f"{d['id']}: tool {t} is avoided")
for s in set(manifest.get("scopes", [])) & set(d.get("forbid_scopes", [])):
failures.append(f"{d['id']}: scope {s} is avoided")
if datetime.date.fromisoformat(str(d["review_by"])) < datetime.date.today():
failures.append(f"{d['id']}: review overdue; decision must be re-confirmed")
return failures
if __name__ == "__main__":
problems = check(sys.argv[1], sys.argv[2])
print("\n".join(problems) or "avoidance register: ok")
sys.exit(1 if problems else 0)
Decision records, review and the alternative path
The decision record should let someone who was not in the room understand and re-test the decision. It needs the risk statement and its scores, the controls considered and why each fell short, the exact scope removed, the sanctioned alternative, the owner, and the conditions under which the decision should be reopened. Those conditions are what turn avoidance from a permanent ban into a managed position: a new model whose injection resistance passes a defined evaluation, a new control such as a transaction signing step becoming available, a regulatory change, or a business case large enough to justify an accepted risk at executive level. Give every record a review date as well, so a decision that was right for this year's models is not still blocking next year's.
Communication matters as much as the record. A flat refusal with no alternative is the most reliable way to create unsanctioned use: the business need does not disappear, so teams reach for a personal account on a public chatbot or a browser extension that nobody has assessed. The reason to pair every avoidance with a sanctioned path is not courtesy; it is that the unsanctioned route usually carries more of the same risk with none of the controls.
Failure modes
- Avoidance theatre. The capability is removed from the UI but the tool remains registered, so a crafted prompt still reaches it. Remove it from the agent definition and the credentials, not just the screen.
- Risk displacement unmeasured. Cutting automation moves volume to humans; if the queue triples and the SLA slips, staff start approving without checking. Score the new path.
- Permanent bans. Decisions with no review date harden into folklore. Teams stop asking, or worse, stop telling.
- Mislabelled mitigation. A tightly guarded capability recorded as 'avoided' makes the register lie; monitoring for it is then never built.
- Vendor drift. A SaaS model or agent platform enables a new feature by default, such as web browsing or memory, and reintroduces an avoided capability without any internal change.
- Over-avoidance. Declining every novel use teaches the organization that the risk function is an obstacle, and the work moves out of its sight.
Trade-offs
| Treatment | Residual for that risk | Ongoing cost | Main weakness |
|---|---|---|---|
| Avoid (decline) | none | lost value | pushes demand elsewhere |
| Avoid (scope cut) | none on that path | enforcement + alternative | displaced work |
| Mitigate | reduced, nonzero | controls + monitoring | depends on control strength |
| Transfer | financial share moved | premiums, contracts | reputation stays with you |
| Accept | unchanged | monitoring | needs authority and appetite |
What to do next
- Pull every register entry whose residual exceeds appetite and decompose each into the capabilities that produce it; the AI risk register article covers how to write those statements so they can be tested.
- Rescore any residual that depends on a model instruction as if the instruction were absent.
- For each remaining out-of-appetite entry, list the scope cuts and what the product loses with each.
- Check thresholds against a written appetite; AI risk appetite shows how to turn board language into numbers.
- Bring decisions to the body with the authority to make them, as described in the AI governance council article.
- Create a machine-readable avoidance register and wire the manifest check into CI.
- Remove avoided capabilities from credentials and network paths, not only from code.
- Pair every avoidance with a sanctioned alternative and an owner for it.
- Set review dates and reopening conditions, and put them on the cycle in AI policy review cadence.
- Report avoidance decisions as part of the program's metrics, alongside the structure in AI governance program structure.