# `semantic.ai_field_refused`

A value from a field marked `to_models: false`, which must never be sent to a model, reaches a prompt or text sent for an embedding.

Area 21, [AI](/docs/ai.md); not declinable; the next step is usually the agent's.

## Why Jadpo refuses it

`to_models: false` on a field says its value is never sent to any
model's provider, whatever personal data `ai: { personal_data: { allowed: } }`
lets through. Use it for a value no provider should ever hold, such as
a patient's NHS number.

The compiler follows each value a prompt is built from back to the
fields it was read from, through variables, functions and text built
from it. So a marked field can't reach a model by being copied first.
The error names the field, never its value.

## What it looks like

The clinic's patients have an NHS number marked `to_models: false`, and
a prompt that checks a referral is given it.





domains/clinic/entities/patient.jadpo:

```jadpo
use clinic/types { ClinicalNote }

/// A patient of the clinic.
entity Patient {
    id: Uuid
    patient_nhs_number: NhsNumber {
        personal: PersonalData.government_id
        to_models: false
    }
    notes: ClinicalNote {
        personal: PersonalData.health
    }

    person: true
}
```


domains/clinic/prompts/check_referral.jadpo:

```jadpo
use clinic/types { ClinicalNote }

/// Checks a referral's notes against the patient it names.
type CheckReferral = Prompt {
    notes: ClinicalNote
    number: NhsNumber
}
```

domains/clinic/prompts/check_referral.prompt:

```jadpo
# Instructions

Say whether the notes are about the patient with this NHS number.

# Request

{number}

{notes}
```

domains/clinic/actions/check_referral_notes.jadpo:

```jadpo
use clinic/entities { Patient, PatientNotFound }
use clinic/types { HandoverSummary }
use clinic/failures { SummaryUnavailable }
use clinic/prompts { CheckReferral }

/// Checks a patient's notes against their NHS number.
action check_referral_notes(
    patient_id: Patient.id
) fails PatientNotFound, SummaryUnavailable -> HandoverSummary {
    var patient: Patient = attempt Patient.get(patient_id)
    return attempt ai.generate(
        CheckReferral {
            notes: patient.notes
            number: patient.patient_nhs_number
        },
        as: HandoverSummary
    ) {
        refused: SummaryUnavailable
        malformed: SummaryUnavailable
        timed_out: SummaryUnavailable
        over_budget: SummaryUnavailable
        too_long: SummaryUnavailable
    }
}
```

```text
error: `Patient.patient_nhs_number` reaches a model, and is `to_models: false`
 --> domains/clinic/actions/check_referral_notes.jadpo:14:21
   |
14 |             number: patient.patient_nhs_number
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^ holds `Patient.patient_nhs_number`
  reason: `to_models: false` keeps a field out of every prompt and every text sent for an embedding, whatever `ai: { personal_data: { allowed: } }` lists; `CheckReferral`'s `{number}` is built from `Patient.patient_nhs_number`.
  next: Leave `Patient.patient_nhs_number` out of `CheckReferral`'s `{number}` (agent)
  rule: semantic.ai_field_refused
  help: https://jadpo.dev/docs/errors/semantic.ai_field_refused

check failed: 1 error, 8 files
```

`(agent)` at the end of the `next:` line means your coding agent can make
the fix itself.

## How to fix it

**If the prompt can do its job without the number**, leave it out. In `domains/clinic/actions/check_referral_notes.jadpo`, the
prompt is built from the notes alone:

```jadpo
    return attempt ai.generate(
        CheckReferral {
            notes: patient.notes
        },
        as: HandoverSummary
    ) {
        ...
    }
```


Then remove the field from the prompt's type and its text.
domains/clinic/prompts/check_referral.jadpo becomes:

```jadpo
use clinic/types { ClinicalNote }

/// Summarises a referral's notes for the clinician taking over.
type CheckReferral = Prompt {
    notes: ClinicalNote
}
```

domains/clinic/prompts/check_referral.prompt:

```jadpo
# Instructions

Summarise the notes for the clinician taking over, in three lines.

# Request

{notes}
```







The model is sent only the notes, and the NHS number stays with your
app. A job that needs the number, such as matching a referral to a
patient, is done in your own code, never by the model.

## Related

- [Personal data sent to a model](/docs/safety-and-privacy.md#personal-data-sent-to-a-model)
- [Following personal data](/docs/following-personal-data.md)
- [Marking personal fields](/docs/marking-personal-fields.md)
- [`semantic.ai_personal_data_not_allowed`](/docs/errors/semantic.ai_personal_data_not_allowed.md)
