Workflow Builder

Guardrail node

Judge a value against a policy you write in plain language, then route to an Allowed or Blocked branch

Purpose

The Guardrail node checks one value against a policy you write in plain language, then routes the workflow down one of exactly two branches: Allowed or Blocked.

Use it where a Condition node cannot help. A Condition node compares values with operators, so it answers questions such as "is confidence above 0.8". A Guardrail node asks a model to judge, so it answers questions such as "does this message contain a credential", "is this request about a competitor", or "does this draft follow our tone of voice".

Typical placements:

  • Right after the Start node, to screen an incoming request before any work is done
  • Just before a Respond node, to check what the workflow is about to send back
  • Around a tool call, so a model cannot be talked into using a tool for something it should not

How it differs from a Condition node

Condition nodeGuardrail node
Decides byOperators on valuesA model judging against your policy
BranchesAs many as you defineExactly two, fixed: Allowed and Blocked
Branch namesYoursFixed labels
ModelOnly for the LLM condition typeAlways
Blocked dataFlows onWithheld, see What a block does to the data

The two-branch shape is fixed, unlike a Condition node whose branch list you define. That is because the node has exactly two handles on the canvas, and those handles are what the branches are. You cannot add a third.

Configuration

The rules the guardrail enforces, written in plain language. This is the whole instruction the model judges against, so be specific about what fails rather than what passes.

Reject the request if it contains an API key, a password, a private key,
or any other credential. Ordinary mentions of security topics are fine.

Write the exception cases in as well. A policy that says only "reject credentials" tends to block any message that mentions the word password.

A template expression selecting the one value to judge. Two forms work:

  • A bare path, which is what the variable picker writes: input.message
  • One or more placeholders inside text: Subject: {{input.subject}}. Body: {{input.body}}

The model sees only this text, so include everything the policy needs to judge and nothing else.

Two branches, labelled allowed and blocked. Both need a target.

Requiring a target on the Blocked branch is deliberate. It makes what a block does a wiring decision you can see on the canvas, rather than a hidden setting. To stop the run on a block, wire Blocked to a failing End node. To answer politely instead, wire it to a Respond node.

The model comes from the Start node

A Guardrail node carries no model selector of its own. It inherits the workflow default model, which is the one you set on the Start node in the canvas.

That keeps a policy check on the same model as the rest of the workflow by default. If you need a guardrail on a specific model, for example a self-hosted one so sensitive text never leaves your perimeter, change the workflow default and pin the nodes that need a different model instead. See Model configuration.

Three outcomes, not two

The node has two branches but three possible outcomes. Keeping them apart is the main thing to understand about this node.

Allowed

The verdict passed. The workflow takes the Allowed target, and the data that arrived at the node passes through to the next node unchanged.

The node also publishes a verdict object:

{
  "_guardrail": {
    "passed": true,
    "reason": null,
    "checked": "the value that was judged"
  }
}

Blocked

The verdict failed. The workflow takes the Blocked target. This is not an error, so it is never retried, and the run continues down the branch you wired.

The output carries only the verdict:

{
  "_guardrail": {
    "passed": false,
    "message": "your custom message, or the generic default"
  }
}

Execution failure

The evaluation could not produce a verdict at all: a timeout, a transport error, an unreadable reply from the model, or no resolvable model configuration.

Neither branch is taken. The activity fails and the workflow's own retry behaviour decides what happens next. A failure is not a block, because nothing was determined.

What a block does to the data

On a block, the node drops the upstream data. It also drops two things it knows:

  • checked, the offending value itself
  • reason, the model's explanation of why the value failed

Both are written to the activity log, so a block is fully auditable. Neither is put in the output.

This is not caution for its own sake. The End node lifts the final activity output into the execution output verbatim, and the model's reason quotes the offending value back. A guardrail that blocked an API key and then returned "the text contains the key sk-abc123" would publish the very credential it just caught.

Withholding is the node's whole purpose. Labelling a violation while still returning it is not a guardrail.

If the Blocked branch needs to say something useful, put it in Custom message, which is published safely as _guardrail.message.

Retry categories

MagOneAI's LLM gateway rewrites every provider error into a sanitised sentence before an activity sees it, because provider error bodies sometimes echo fragments of API keys. By the time an error reaches this node, the status code and the provider's original wording are gone.

That is why you pick a category rather than typing a pattern. A pattern such as 429|timeout would match nothing, and you would silently get no retries at all.

CategoryLabel in the UICovers
rate_limitRate limit (429)The provider throttled the request. Usually clears on retry.
provider_unavailableProvider unavailable (5xx)An upstream outage or overload.
timeoutTimeoutThe request or the guardrail evaluation ran out of time.
connectionConnection errorA network failure reaching the provider.
unreadable_verdictUnreadable verdictThe model replied with something that was not a usable pass or fail.

A sensible starting point is the four transient categories: rate_limit, provider_unavailable, timeout and connection.

An exhausted provider account is not a rate limit. Running out of credit also arrives as a 429, but retrying cannot fix it, so it is deliberately outside the rate_limit category.

unreadable_verdict is usually a model or prompt problem, not a blip. If you see it often, the model is too small for the policy or the policy is ambiguous. Retrying hides the cause.

An unknown category is refused when you save the node, rather than ignored at run time, where a typo would look exactly like "never retries".

Examples

Example 1: Screen an incoming request for credentials

{
  "id": "credential-check",
  "type": "guardrail",
  "config": {
    "policy": "Reject the request if it contains an API key, password, private key, or access token. Discussing security topics in general is acceptable.",
    "input_to_validate": "{{input.message}}",
    "branches": [
      { "label": "allowed", "goto": "handle-request" },
      { "label": "blocked", "goto": "refusal-response" }
    ],
    "custom_message": "Please remove any credentials from your message and try again.",
    "timeout_seconds": 10,
    "max_attempts": 3,
    "retry_on_errors": ["rate_limit", "timeout", "connection"]
  }
}

Example 2: Check a draft before it is sent

Place the guardrail between the agent that writes the reply and the Respond node that sends it.

{
  "id": "tone-check",
  "type": "guardrail",
  "config": {
    "policy": "Reject the draft if it promises a refund, a discount, a delivery date, or any other commitment on the company's behalf. Apologies and explanations are acceptable.",
    "input_to_validate": "draft-reply.text",
    "branches": [
      { "label": "allowed", "goto": "send-reply" },
      { "label": "blocked", "goto": "escalate-to-human" }
    ]
  }
}

Wiring Blocked to a Human task node rather than a refusal turns the guardrail into a review trigger. See Human task node.

Example 3: Stop the run on a block

To end the execution instead of answering, wire the Blocked branch to an End node configured to fail. The block is still visible in the execution timeline, and _guardrail.message is still published.

Best practices

Troubleshooting

Next steps

MagOneAI© 2026 Magure, Inc.

On this page