Skip to content
KONIGI

Refusal

The assistant will not do the thing, and the viewer needs to know whether to rephrase or give up.

Updated September 12, 2026

Problem

The viewer asked for something and got a polite paragraph explaining that it won’t be provided. They can’t tell whether the request was outside the product’s abilities, against its rules, or simply misunderstood, and those three call for completely different next moves.

Solution

Separate the three cases, because collapsing them into one apologetic voice is what makes this pattern so disliked.

Cannot is a capability limit. The assistant has no access to that system, cannot see that file type, cannot browse. The useful response names the limit and points at whatever does work.

Will not is a policy decision. The request falls outside what the product allows. The useful response says that plainly, in one sentence, without explaining ethics to an adult.

Does not know is a knowledge limit. The answer is outside training data, past a cutoff, or not in the retrieved corpus. The useful response says so and offers to look, and this case is the one most often disguised as the other two.

Each of the three deserves a different sentence and a different offer, and the offer is what turns a refusal into a usable turn. HAX guideline 11 asks systems to make clear why they did what they did, and a refusal is the moment a viewer most wants that and is least often given it. PAIR’s chapter on graceful failure supplies the rest of the shape: explain, then give a path forward.

Tone is the most complained-about property of this pattern anywhere in the field. A refusal that moralises, restates the request back as if it were sinister, or lectures about responsibility produces more damage to trust than the refusal itself. State the limit and offer the nearest thing available. Length is a tell here, since a long refusal is almost always a defensive one.

False positives are the real cost and the one the metrics hide. A refusal on a legitimate request teaches the viewer that the product is unreliable for a whole category of work, and they don’t come back later to test whether the boundary moved. Because they stop asking, the failure never appears in the logs as a failure, which makes this the most under-measured problem in the pattern. An appeal path, even a simple report control that a human reads, is the only cheap way to find out how often it happens.

Partial compliance beats refusal wherever it’s possible. Answering the safe eighty per cent of a request and naming the part that was withheld leaves the viewer better off than a blanket decline, and it makes the boundary legible in a way the viewer can work with.

Use when

The system has a real limit, whether that limit is capability, policy or knowledge.

Don’t use when

The request is answerable with a caveat. Reaching for a refusal where a hedged answer would serve is the habit that trains people to take their work somewhere with fewer rules.

Trade-offs

Naming the category is more useful and tells a determined person exactly which wall they hit, which is a genuine adversarial cost. Offering an alternative is kinder and occasionally reads as evasion when the alternative is much weaker than the request. Short refusals are better and can read as curt where a viewer expected an explanation. A per-category message is more useful than a generic one, and it creates a maintenance surface that drifts out of step with the policy it describes.

Checklist

  • Can the viewer tell whether this is a capability, policy, or knowledge limit?
  • Is there an offer of the nearest thing the assistant can do?
  • How long is the message, and does it moralise?
  • Is partial compliance possible instead of a full decline?
  • Is there a way to report a wrong refusal, and does a human see it?
  • Is the false-positive rate measured at all?
  • Does the refusal contradict what the first-run state or the prompt starters promised?
  • Does it read the same way on the tenth occurrence as the first?
  • Is a policy decline visually distinguishable from an error?
  • Can the viewer tell whether rephrasing would help?

Compare

Claude tends toward explaining the boundary and offering an adjacent version of the request. The turn stays productive, and it runs long. ChatGPT favours shorter declines with a suggested reframing, so the next move is usually visible in the message itself. Gemini more often redirects to search for knowledge-limited cases, converting a decline into a handoff rather than an ending. Microsoft Copilot in an enterprise tenant has a fourth case the consumer products lack, where the refusal is about the viewer’s permissions rather than the model’s policy, and saying which one it is matters more there than anywhere else.

Generation error looks similar on screen and means the system broke rather than declined. Knowledge cutoff notice covers the third category before a question is even asked. First-run state is where the limits should have been set out in advance. Prompt starters create the expectations a refusal disappoints. Usage meter is the other kind of no, where the limit is quota rather than policy.

Refusal anatomy Three declines separated by cause: a capability limit, a policy decision and a knowledge gap, each with a different sentence and a different offer, alongside a long moralising version shown as the failure case and a report control for wrong refusals. Cannot, will not, and does not know Cannot no access to that system. Paste it in? Will not outside what this product allows. Does not know past the cutoff. Search for it? length is the tell wrong refusal? people who hit one stop asking, so it never shows up in the logs 1 2 3 4 1 NAME THE CATEGORY Capability, policy and knowledge call for three different next moves. 2 OFFER THE NEAREST THING The offer is what turns a decline into a usable turn instead of an ending. 3 DO NOT MORALISE Restating the request back as sinister damages more trust than the decline does. 4 FALSE POSITIVES The most under-measured problem here. A report control is the cheap way to see it. Partial compliance beats a blanket decline. Answer the safe part and name what was withheld.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

Cannot, will not, and does not know are three different sentences with three different offers. Length is the tell for the moralising version, and people who hit a wrong refusal stop asking, so it never shows up in the logs.

shadcn
npx shadcn@latest add button
Tokens
--card--card-foreground--muted-foreground--border--primary

Cannot

no access to that system.

Will not

outside what this product allows.

Does not know

past the cutoff.

Refusal.tsxA closed set of three causes, an offer that is required for two of them, a length guard, and a report control on every one.

import { Button } from "@/components/ui/button";

/**
 * Three declines with three causes, and the cause is the first thing the
 * viewer needs. A capability limit, a policy decision and a knowledge gap
 * call for different next moves, so the set is closed and the label is
 * derived from it rather than typed each time.
 */
export type RefusalKind = "cannot" | "will-not" | "does-not-know";

const KIND_LABEL: Record<RefusalKind, string> = {
  "cannot": "Cannot",
  "will-not": "Will not",
  "does-not-know": "Does not know",
};

/** The nearest thing the assistant can do instead. Paste it in, search for it. */
export type Offer = { label: string; onSelect: () => void };

/**
 * Length is the tell. A decline that runs past a sentence or two is almost
 * always a defensive one, so anything longer than this is flagged in
 * development rather than shipped as a paragraph.
 */
const MAX_REASON = 120;

type Props = {
  /** One plain sentence. No apology, no restating the request as sinister. */
  reason: string;
  /** Wrong refusals never show up in the logs, because people stop asking.
   *  A report control a human reads is the only cheap way to see them. */
  onReport: () => void;
} & (
  /** A capability or knowledge limit always has a nearest thing to offer. */
  | { kind: "cannot" | "does-not-know"; offer: Offer }
  /** A policy decline may have nothing to offer, and says so in one line. */
  | { kind: "will-not"; offer?: Offer }
);

export function Refusal({ kind, reason, offer, onReport }: Props) {
  if (reason.length > MAX_REASON) {
    console.warn(`Refusal: "${reason.slice(0, 40)}…" is ${reason.length} characters. Cut it to one sentence.`);
  }
  return (
    <div className="rounded-lg border bg-card p-3 text-card-foreground">
      <p className="text-[11px] uppercase tracking-wide text-muted-foreground">{KIND_LABEL[kind]}</p>
      <p className="mt-1.5 text-sm">
        {reason}
        {offer && (
          <>
            {" "}
            <Button variant="link" size="sm" className="h-auto p-0 text-sm" onClick={offer.onSelect}>{offer.label}</Button>
          </>
        )}
      </p>
      <Button variant="outline" size="sm" className="mt-3 h-6 text-[11px] font-normal text-muted-foreground" onClick={onReport}>
        wrong refusal?
      </Button>
    </div>
  );
}

demo.tsxHow it is called: one of each kind, with the offers and the report handler a real app would supply.

import { Refusal } from "./Refusal";

/**
 * The closed set, one of each. Cannot and does-not-know carry an offer;
 * the policy decline is one line and stops there. Every one has the report
 * control, because the wrong ones are the ones nobody measures.
 */
export default function Demo() {
  const report = (kind: string) => () => console.log("reported a wrong refusal:", kind);
  return (
    <div className="flex w-[320px] flex-col gap-3">
      <Refusal
        kind="cannot"
        reason="no access to that system."
        offer={{ label: "Paste it in?", onSelect: () => {} }}
        onReport={report("cannot")}
      />
      <Refusal
        kind="will-not"
        reason="outside what this product allows."
        onReport={report("will-not")}
      />
      <Refusal
        kind="does-not-know"
        reason="past the cutoff."
        offer={{ label: "Search for it?", onSelect: () => {} }}
        onReport={report("does-not-know")}
      />
    </div>
  );
}
What it renders. Identical markup in both panes, with only the token values changing.

Examples

No captures reference this pattern yet. Captures arrive product by product; see Products for what's in the gallery so far.