Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 5 Nov 2026. What's built · Install 0.3.0
jadpo
ReferenceErrors

ErrorsPolicy · rule

policy.effect_ungranted

On this page

A route runs an action that nobody is allowed to call, because nothing in the app says who may.

Area 9, Policy; not declinable; the next step is usually the human's.

Why Jadpo refuses it

An action takes its permission from the stored records it reads and changes: each entity's grants say who may create, read, update or delete its records. An action that reaches no entity, such as a health check, has no grants to take, so nobody may call it. The same happens when a route reaches a create, read, update or delete that no grant names. A route nobody may call answers every request with 403, so Jadpo refuses it when you check, rather than when someone calls it.

Jadpo won't choose who may call the action for you. Root policy { } lists such actions under actions:, each with who may run it, and that list decides who can reach your app's data and behaviour.

What it looks like

main.jadpo:

example
type Health = Output {    ok: Bool}action check_health() -> Health {    return Health {        ok: true    }}routes Checkup {    prefix: /health    POST / {        authentication: RouteAuthentication.none        output: Health        run: check_health()    }}
jadpo checkcheck failed: 1 error, 1 file

error POST /health runs check_health, which nobody may call

--> main.jadpo:14:5

   |14 |     POST / {   |     ^^^^^^ reaches no entity
reason
An action takes its permission from the entities it reaches, and check_health reaches none, so it is refused to everyone unless root policy { } lists who may call it under actions:.
next
List check_health: [...] under actions: in policy.jadpo (human)
rule
policy.effect_ungranted
help
https://jadpo.dev/docs/errors/policy.effect_ungranted

(human) at the end of the next: line tells your coding agent to stop and ask you which fix you want, because the choice is yours to make: it decides who may call check_health.

A line under actions: can also let nobody in. A user field such as Note.owner_id, or a role held in a record, is checked on the records the action itself creates, changes or deletes. An action that changes none of them, such as a summary of the caller's notes, passes nobody through such a line, so its route could never succeed. Here, note_tidings changes no note.

main.jadpo:

example
type Handle = Text {    max_length: 40}entity User {    id: Uuid    handle: Handle {        unique: true    }}principal {    user: {        subject: User.handle    }}authentication {    api_token: {}}entity Note {    id: Uuid    owner_id: User.id {        on_delete: OnDelete.cascade    }    policy: {        read: [owner_id]    }}type Tidings = Output {    ok: Bool}action note_tidings() -> Tidings {    return Tidings {        ok: true    }}routes Bulletin {    prefix: /tidings    POST / {        output: Tidings        run: note_tidings()    }}policy {    actions: {        note_tidings: [Note.owner_id]    }}
jadpo checkcheck failed: 1 error, 1 file

error POST /tidings runs note_tidings, which nobody may call

--> main.jadpo:46:5

   |46 |     POST / {   |     ^^^^^^ nobody passes its line
reason
note_tidings's line under actions: names Note.owner_id, the caller a Note the action changes points at, and note_tidings changes no such record itself, so no caller passes the line as the action starts and this route could never succeed.
next
List Access.authenticated or a role held across the whole application for note_tidings under actions: (human)
rule
policy.effect_ungranted
help
https://jadpo.dev/docs/errors/policy.effect_ungranted

How to fix it

The question is who should be able to call the action.

If anyone may call it, signed in or not, list it for Access.public. In a project, this goes in policy.jadpo, beside app.jadpo, with the action's domain in front of its name (monitoring.check_health):

example
policy {    actions: {        check_health: [Access.public]    }}

POST /health now answers anyone. Its route says authentication: RouteAuthentication.none, so no caller has to sign in.

If anyone signed in may call it, list it for Access.authenticated:

example
policy {    actions: {        note_tidings: [Access.authenticated]    }}

Every signed-in caller may now call POST /tidings. Its reads still follow Note's grants, so each caller sees only the notes they own.

To let in only some people, list a role they hold across the whole application, such as StaffRole.operator, a membership whose scope is the application (A membership role). A role held in one record lets nobody in here, for the reason above.

Markdown for agents: /docs/errors/policy.effect_ungranted.md.Built from Jadpo main at aee2e8f.