Skip to main content
3Nsofts logo3Nsofts
Foundation ModelsUpdated · June 2026

Fixing guardrailViolation in Foundation Models: False Positives, the Locale Trap, and Fallbacks

Author
Ehsan Azish · 3NSOFTS
Updated
June 2026
Read time
13 min read
Level
Intermediate
Platform
iOS 26+, Foundation Models, Xcode 26

Implementation Notes

  • ~/ What broke: Harmless prompts can look unsafe when model assets, locale, or fallback state are wrong.
  • ~/ What to do: Add locale checks, user-safe fallbacks, and retry copy that does not hide real safety failures.
guardrailViolationFoundation Models guardrail errorApple Intelligence model not downloadingSystemLanguageModel availabilityon-device LLM safety error

LanguageModelSession.GenerationError.guardrailViolation is the most confusing error in the Foundation Models framework, because it fires in two completely unrelated situations:

  1. A genuine safety rejection — the prompt or the generated output tripped Apple's on-device content guardrails.
  2. An asset/configuration failure that misreports itself as a guardrail violation — the model never even ran.

If you treat every guardrailViolation as "my prompt was unsafe," you'll waste hours sanitizing prompts that were never the problem. This guide separates the two and gives you a recovery path for each.

The locale trap (this one is not your code)

Here's a real failure that looks like a guardrail rejection but isn't:

guardrailViolation(... debugDescription: "There are no underlying assets
(neither atomic instance nor asset roots) for consistency token for
asset set com.apple.modelcatalog")

SystemLanguageModel.default.availability returns .available. Your prompt is completely benign. And it still throws.

The cause: a mismatch between the system language and the Siri language. If macOS is set to English (US) but Siri defaults to English (Australia) — or any similar pairing — Apple Intelligence never offers the model download, the model catalog assets are absent, and the framework surfaces the missing-asset failure as a guardrail violation.

The fix has nothing to do with code. On the affected machine:

  1. Align the Siri language with the system language (e.g. both English — United States).
  2. Confirm the Apple Intelligence toggle now appears and the models download.
  3. Re-run.

Document this for your QA testers and TestFlight users, because they will report it as "the AI feature is broken" and you'll burn a debugging cycle chasing a prompt problem that doesn't exist.

Telling a real safety rejection from an asset failure

Inspect the error's context before reacting:

do {
    let answer = try await session.respond(to: prompt)
} catch let error as LanguageModelSession.GenerationError {
    switch error {
    case .guardrailViolation(let context):
        let detail = context.debugDescription.lowercased()
        if detail.contains("asset") || detail.contains("modelcatalog") {
            // NOT a safety rejection — model assets are missing/unavailable.
            // Route to the availability/onboarding path, not prompt sanitization.
            handleModelUnavailable()
        } else {
            // Genuine content guardrail — the prompt or output was flagged.
            handleSafetyRejection(for: prompt)
        }
    default:
        handleOther(error)
    }
}

Matching on debugDescription is admittedly fragile — Apple may reword it — so keep this classifier in one function. But it's the only signal available to distinguish the two cases today, and it saves real debugging time.

Reducing genuine false positives

When the guardrail genuinely fires on content you consider safe, the on-device model is being conservative. Production tactics, in order of preference:

1. Constrain the task with guided generation. Free-form prompts give the guardrail more surface area to object to. A @Generable schema narrows what the model produces and how, and tends to trip fewer false positives than open-ended text generation.

@Generable
struct ExtractedFields {
    @Guide(description: "The product name only")
    let name: String
    @Guide(description: "Price as a number, no currency symbol")
    let price: Double
}

let result = try await session.respond(
    to: "Extract fields from: \(userText)",
    generating: ExtractedFields.self
)

2. Rephrase system instructions to set context. A clear instruction that frames the task as benign ("You extract structured data from receipts") gives the guardrail context that a bare prompt lacks.

3. Pre-classify risky input. If your domain naturally includes content that might trip the guardrail (health, legal, anything sensitive), screen input on a fast path before sending it, and route flagged input to a non-AI fallback rather than eating a thrown error.

The fallback strategy: never dead-end the user

A guardrailViolation should degrade gracefully, not show an error dialog. Design every AI feature with a deterministic fallback that runs when generation is refused:

func generateSummary(for text: String) async -> String {
    do {
        return try await session.respond(
            to: "Summarize concisely:\n\(text)"
        ).content
    } catch LanguageModelSession.GenerationError.guardrailViolation {
        // Deterministic fallback — no model, no failure surfaced to the user.
        return deterministicSummary(of: text)   // e.g. first N sentences, key lines
    } catch {
        return deterministicSummary(of: text)
    }
}

The principle: the AI path is an enhancement, not a dependency. If the guardrail refuses, the feature still produces something useful. This is also the right architecture for the large population of devices that can't run the model at all (see the availability-gating guide).

Production checklist

  • Classify before reacting — asset/catalog failures masquerade as guardrail violations.
  • Document the Siri/system language fix for QA and support; it's a configuration issue, not a bug.
  • Prefer guided generation over free-form prompts to shrink the guardrail's objection surface.
  • Always ship a deterministic fallback so a refusal degrades instead of dead-ending.
  • Keep the debugDescription classifier in one function — it's fragile by necessity.

Why this matters for shipped apps

Guardrail behavior is non-deterministic from your code's perspective — the same prompt can pass on one OS point release and trip on another. An app that treats the AI output as guaranteed will show users error dialogs in the wild. An app that treats it as a best-effort enhancement with a real fallback never breaks. That architectural decision is made before you write the first respond(to:) call.

Building on Foundation Models and want the safety/fallback architecture reviewed before launch? That's core to our on-device AI integration work at 3NSOFTS.