- Security & Policies
- Policy set errors
Policy set errors
Every 4xx and 503 answer of the policy-set routes carries a machine-readable code. The SDKs expose the codes as POLICY_SET_ERROR_CODES (JavaScript) and as PolicySetErr* constants (Go).
An error body has this shape:
{ "error": "policy_set_builtin_forbidden: authz.rego:7:3: builtin http.send is not allowed in a policy set", "message": "policy_set_builtin_forbidden: authz.rego:7:3: builtin http.send is not allowed in a policy set", "code": "policy_set_builtin_forbidden", "details": { "errors": [ { "file": "authz.rego", "line": 7, "col": 3, "message": "builtin http.send is not allowed in a policy set" } ] }, "retryable": false}A validation error (HTTP 400) lists one entry in details.errors for each problem. An entry has file, line, col and message. A problem that is not in one file has only message. One failure rejects the whole set.
Validation errors (HTTP 400)
Section titled “Validation errors (HTTP 400)”These codes come from PUT, rollback, validate and dry-run with a candidate set.
| Code | Cause | Fix |
|---|---|---|
policy_set_compile_error | A file does not parse as Rego, or the set does not compile (unknown function, unsafe variable, recursion). The compiler may also have failed with an internal error. | Read details.errors for the file and line. Run opa check locally. |
policy_set_v0_syntax | A file uses Rego v0 syntax. Ironflow reads Rego v1 only. | Run opa fmt --v0-v1 -w <dir>. The command changes the syntax and keeps the logic. |
policy_set_reserved_namespace | A file declares a package under data.ironflow, other than ironflow.authz or ironflow.authz_test. | Use package ironflow.authz for the entry point, or a package outside ironflow. |
policy_set_builtin_forbidden | A file calls or references a banned builtin: http.send, net.lookup_ip_addr, opa.runtime, json.match_schema, json.verify_schema, bits.lsh, regex.replace, regex.split, regex.find_n, regex.find_all_string_submatch_n, regex.template_match or indexof_n. | Remove the call. See Author a policy set. |
policy_set_sprintf_format | The first argument of sprintf is not a string literal, has a * width, has a width or precision above 9999, or has more than 32 verbs. sprintf is also not allowed as a with value. | Write the format as a literal within the limits. |
policy_set_schema_error | A rule in package ironflow.authz does not type-check against the input schema. | Fix the field name or type at the given line. |
policy_set_entrypoint_type | deny is not a partial set rule, or the decision rule is a set or a function, or its type is not a boolean or an object. | Write deny contains <reason> if { ... }. Write the decision rule as a complete rule, for example allow := .... |
policy_set_bad_file_name | A file name is not a relative path of [A-Za-z0-9_.-] segments that ends in .rego, has a . or .. segment, is in a .ironflow or .git directory, or differs only in case from another name. | Rename the file. |
policy_set_too_large | The set has more than 500 files, or more than 2 MiB of source. With HTTP 413, the request body is larger than the limit. | Remove files or split the work. |
policy_set_compile_timeout | Compiling the set took longer than 2 seconds. | Simplify the set. Remove very large generated files. |
policy_set_bad_mode | The mode is not enforce or monitor. | Use one of the two values, or omit the field. |
policy_set_bad_decision | The decision path is not a rule path under data outside data.ironflow, or the rule is not defined. | Name an existing rule, for example data.envoy.authz.allow. |
policy_set_bad_request | The request is malformed: invalid JSON, an unknown field, invalid UTF-8, an If-Match value that is not a version number, a version in the path that is not a positive integer, or a dry-run input without request and subject objects. | Fix the request. |
Access, conflict and rate errors
Section titled “Access, conflict and rate errors”| Code | HTTP | Cause | Fix |
|---|---|---|---|
policy_set_forbidden | 403 | A platform route was called without platform credentials, or a tenant route was called without an organization. The platform set is not reachable through the tenant routes. | Use platform credentials for /api/v1/platform/policy-set. |
policy_set_not_found | 404 | The version does not exist, the organization has no set for a dry-run of the active set, or the organization was not found (force-monitor). | List the versions. Send set in the dry-run to test a candidate. |
policy_set_version_conflict | 412 | If-Match does not match the active version. Another admin wrote first. | Run ironflow policy pull, merge your changes and push again. |
policy_set_precondition_required | 428 | A PUT or rollback has no If-Match header. | Send the version that you read (0 when the organization has no set). The header accepts 3, "3" and W/"3". |
policy_set_self_lockout | 422 or 503 | The preflight found that the candidate set would deny a subject who must keep write access, or it could not decide. The body names blocking_subject, reason and detail. A store failure or a timeout answers 503, which you can retry. | Fix the rules. As a last step, use the bypass. See Recover from a lockout. |
policy_set_rate_limited | 429 | The organization wrote too fast (one write every 6 seconds, burst 5), or called validate or dry-run too fast (one call every second, burst 10). The limits apply to each node. | Wait for Retry-After seconds. |
policy_set_busy | 429 | Four compiles are already running on this node. | Retry after one second. |
policy_set_not_propagated | 503 | The write committed, but this node could not finish adopting it. Other nodes may still use the old set. The error is not retryable. | See below. |
policy_set_not_propagated
Section titled “policy_set_not_propagated”The message says saved as version N but not yet active on every node; send the same request again with If-Match: N (a no-op that finishes propagation).
The write is stored as version N. Do not resend with the old version, because that answers 412. Send the same request again with If-Match: N. The server sees that the set is unchanged and finishes propagation. The CLI records N in .ironflow-policy.json and tells you to run the same command again. The SDKs mark the error as not retryable, so a retry loop does not resend it with the old version.
The same recovery applies to a rollback and to force-monitor.
Self-lockout answers
Section titled “Self-lockout answers”The body of policy_set_self_lockout has an error field with one of two values:
error | HTTP | Meaning |
|---|---|---|
self_lockout_preflight_failed | 422 | The candidate set denies the named subject a read or write action on the policy set (GET or PUT /api/v1/policy-set, or GET /api/v1/policy-set/versions). |
self_lockout_preflight_indeterminate | 422 or 503 | The preflight could not decide. A store failure, or one evaluation that timed out, is 503. A set that fails to evaluate, or a budget that ran out (10,000 evaluations or one second of evaluation), is 422. |
Evaluation errors at request time
Section titled “Evaluation errors at request time”These are not API errors. They appear as audit reasons. See Debug a deny.
| Audit reason | Meaning |
|---|---|
policy evaluation error (builtin_error) | A builtin failed. |
policy evaluation error (conflict) | A complete rule produced two values. |
policy evaluation error (timeout) | The set ran longer than 50 ms. |
policy evaluation error (panic) | The evaluator failed with a panic. |
policy evaluation error (eval_error) | Another evaluation error. |
policy evaluation error (compile_error) | The stored set does not compile on this node. |
policy evaluation error (load_error) | The node could not read the set from the store. |
non-string deny reason | A deny member was not a string. |