Errors and limits
Errors in this block come from four places: the rules.svc HTTP surface, loading a rule set (on
either surface), executing it, and the kis CLI with its document tools. This page lists them with
the text you see. For symptom-first diagnosis, start at
Troubleshooting.
Two facts govern the rest:
- A rule that fails stops the execution. Nothing after it runs, whatever its salience. What the
rules had written before it is not lost: rules.svc returns it under the error’s
meta.partial. - Most mistakes fail the load, not a request. Every call a rule makes is checked when the rule set loads, so a misspelt method or a wrong number of arguments never reaches traffic.
rules.svc
Section titled “rules.svc”Every error the service raises answers with its own status and a code, in the body and in the
X-Kis-Error-Code header:
{"error":{"code":"rules-scope-not-found","context":null,"message":"the product has no scope nope: no static scope of rules/rules.yaml and no folder rules/nope/","meta":null}}Branch on the code; the message is for people. Two answers come from the platform’s middleware before the service sees the call, and are plain text:
| Status | Body | Why |
|---|---|---|
401 | Unauthorized | no token, or one that does not verify |
400 | invalid tenant | the tenant headers are missing or do not match the token |
404 | empty | no such route |
| Code | Status | When |
|---|---|---|
rules-json-request-parse-failed | 400 | the body is not JSON, or holds a key the route does not read (json: unknown field "audits") |
rules-body-too-large | 413 | the body is over rules.body.maxbytes |
rules-name-missing | 400 | no scope (or no name on POST /rule) |
rules-facts-invalid | 400 | a fact named like one the engine binds; facts a static scope’s contract refuses; a fact named like the scope’s knowledge or record; ids empty or over rules.evaluate.maxids; a sweep’s where that does not parse; evaluating or sweeping a scope without data.over |
rules-tenant-not-served | 400 | the tenant is not in rules.svc’s configuration |
rules-tenant-not-loaded | 503 | the tenant’s rules have not loaded yet. Comes with Retry-After |
rules-scope-not-found | 404 | no static scope and no top folder of that name |
rules-access-denied | 403 | the caller may not do this (Access) |
rules-execution-failed | 422 | a rule failed; the message names it, and meta.partial holds what was written before. Also knowledge that matched more rows than its max |
rules-execution-timeout | 504 | the execution ran past its timeout |
rules-source-unavailable | 503 | a source the scope reads could not be reached |
rules-source-denied | 403 | a source refused the caller |
rules-write-not-declared | 422 | a proposal the scope’s writes do not allow (a record’s error, in evaluate and sweeps) |
rules-write-conflict | 409 | a record changed while it was being decided, twice (a record’s error) |
rules-write-partial | 207 | a record’s writes applied in part (a record’s error) |
rules-sweep-not-found | 404 | no sweep of that id that the caller may see |
rules-sweep-limit | 429 | the tenant has rules.sweeps.pertenant sweeps queued or running |
rules-sweeps-disabled | 409 | sweeps are off, or no system user is configured |
POST /rules/evaluate answers 200 even when records fail: each failing record carries
error: {code, message} with one of these codes (Evaluating records).
A watched run (POST /rules/run) returns a rule failure or a load failure in its error field with
200, because whoever watches it needs to see it. POST /rules/events answers data.svc with
statuses only (Events).
A failed execution, with what it wrote before the failure. Rule Currency (salience 10) had set
currency; rule Tier then passed 10000 where math.Min takes a float:
{"error":{"code":"rules-execution-failed","context":{},"message":"executing ruleset invoices/**: error while executing rule Tier. got rule engine execute panic on rule Tier ! recovered : reflect: Call using int64 as type float64","meta":{"partial":{"currency":"EUR"}}}}What fails a load
Section titled “What fails a load”A tenant’s rules load when its rules/ folder changes; kis rules and kis rules check load them
on the command line. A load that fails changes nothing on rules.svc: the tenant keeps serving what
it had, the failure is logged, and GET /rulesets says why in error.
| Message | Cause |
|---|---|
<rule>:<line>: error: <obj> has no method <Fn> (did you mean <Gn>?) | a method that does not exist on a bound object |
<rule>:<line>: error: <obj>.<Fn> takes N argument(s), called with M | a wrong number of arguments |
<file>: got N error(s) in grl the script | the rule text does not parse: a syntax error, a missing ;, a construct the language does not have, two rules with the same name. The count is all the parser reports; bisect by file |
rules/rules.yaml: … | a static scope that does not hold (Scopes) |
its rules propose what it does not declare under data.writes | a literal act.* call outside the scope’s writes |
rules/extend/access.yaml: … | an unknown action or key, an empty expression, a language other than expr |
a .grl file directly in rules/ or in rules/extend/ | rules live in folders under rules/ |
A load also reports warnings, which do not fail it: a deprecated call, with its replacement
(in.GetStr is deprecated; use in.Str).
Constructs that fail the parse because the language does not have them:
| Written | Write instead |
|---|---|
findings.Add(Finding{...}) | findings.Add(ruleID, severity, category, message, fields), or findings.Error/Warn/Info/Critical |
if cond { … } else { … }; | two rules with opposite conditions at different salience |
two conditions on separate lines in when | one expression, joined with && or || |
a then statement without its ; | every statement ends in ;, the last one too |
Execution errors
Section titled “Execution errors”These are found only when a rule runs. Each stops the execution; rules.svc answers 422 rules-execution-failed with the message, and kis rules fails that input.
| Message | Cause |
|---|---|
got left hand expression error. got non existent key <fact> | a when names a fact the call did not send, in any rule, even one that would not have fired. Read optional facts through in.* with a default: in.Str("note", "") |
error while executing rule <R>. got non existent key <fact> | the same, in a then |
this node identified as "<fact>" is not referencing to an object | dot access on a map fact (order.Total). Use in.Double("order.Total", 0.0) |
reflect: Call using int64 as type float64 | a whole-number literal passed to a float parameter: write 10.0, not 10 |
reflect: Call using int64 as type int | the parameter takes a narrower integer than a rule literal can be. Nothing to fix in the rule: report it |
reflect: call of reflect.Value.Type on zero Value | a comparison with a missing value: out.Get("k") == "v" on an unset key. Use the typed reader with a default: out.Str("k", "") == "v" |
reflect: Call using zero Value argument / <var> is not an array nor map | a local variable holding a map or a list. Pass the call straight in, or build a map with map.Of(…) in place |
this code part should not be reached | index assignment into a local variable (m["k"] = 1) |
this node identified as "DEFUNC" have no function named <Fn> | an unqualified call that is no built-in: a helper needs its namespace (strings.Contains). Unqualified calls are not checked at load |
… returns multiple values, multiple value returns are not supported | a method that returns a value and an error, such as out.JSON(). Not callable from a rule |
the GruleEngine successfully selected rule candidate for execution after N cycles | a rule calling agenda.Repeat() whose when stays true: the cycle cap stopped it |
execution timed out | the execution ran past its timeout (504 rules-execution-timeout on rules.svc) |
Panics inside a rule are recovered and reported with the rule’s name: rule engine execute panic on rule <R> ! recovered : … in a then, error while evaluating rule <R> ! recovered : … in a when.
Every reflect: row above arrives inside one of those.
The multiple-values message arrives across several lines; match on returns multiple values.
CLI errors
Section titled “CLI errors”Everything below exits 1 and writes to stderr unless stated otherwise. A run ended by a signal exits with 128 plus the signal number.
| Message | Cause |
|---|---|
Error: 'rules' is not installed. | kis rules and kis bbox run a separately installed plugin, kis-rules (kvm install kis-rules). The message names the command you typed. The search order is the directory holding kis, ~/.kisai/bin, $KIS_PLUGIN_PATH, then $PATH |
required flag(s) "rules" not set | -r/--rules omitted |
loading rules: … | a rule file failed to load: the messages under What fails a load |
no files matched pattern <glob> | the -i directory and -p pattern selected nothing |
no inputs resolved (…) | nothing matched. With --require-bbox, every candidate lacked a .bbox sidecar; a preceding warning: line names what was dropped |
invalid --skip-pattern "<p>": <err> | a malformed glob, checked before the run starts |
ERROR: <path>: <err> | one input failed. Printed as it happens; the batch continues |
N file(s) failed to process / N prefix(es) failed | the final error of a batch with at least one failure |
A batch never stops early: every input is attempted, and the exit code is 1 if any failed, so with
--update the files that succeeded are already rewritten. See the CLI.
Document errors
Section titled “Document errors”Documents are read by kis rules and the kis bbox tools. rules.svc serves no documents today.
Reading, parsing or analysing a document fails before any rule fires.
| Message | Cause |
|---|---|
plugin bbox prepare: loading document <name>: <err> | a document could not be read, parsed or analysed |
duplicate schema name "<n>" (from <a> and <b>) | two --schema/--schemas files share a basename; a schema’s name is its file name without the extension |
unsupported top-level type "<t>" (expected object or array) | a schema whose top level is neither |
array-typed schema must have an "items" object | an array schema without items |
at least one input file is required (use positional args or -i flag) | a kis bbox subcommand with no inputs |
invalid snippet definition "<s>": expected "name|start phrase|end phrase" | a --snip value that is not three |-separated parts |
table has no headers | kis bbox table -H found a table with no header row |
missing headers: <a> (found: <b>) | header validation failed against the detected table |
Limits
Section titled “Limits”| Limit | Default | Set by | When it trips |
|---|---|---|---|
| Execution time | 10s | rules.exec.timeout, a static scope’s limits.timeout (capped by rules.exec.maxtimeout, 30s) | 504 rules-execution-timeout |
| Cycles | 5000 | rules.exec.maxcycles, a static scope’s limits.maxcycles | 422 rules-execution-failed, with the partial output |
| Request body | 1 MiB | rules.body.maxbytes | 413 rules-body-too-large |
| Named facts per call | 20 | — | 400 rules-facts-invalid |
| Records per evaluate | 100 | rules.evaluate.maxids | 400 rules-facts-invalid |
| Knowledge rows | 1000, or the read’s max | the scope | 422 rules-execution-failed |
| A watched run | 10s, 2000 cycles; a draft at most 200 files and 4 MiB | fixed | the run’s error |
| Sweeps per tenant | 1 at a time | rules.sweeps.pertenant | 429 rules-sweep-limit |
A cycle counts only when a rule fires, and each rule fires at most once per execution unless it calls
agenda.Repeat(), so the cycle cap is reached only by a rule that repeats. The time limit is what
bounds a slow rule.
Failures that produce no error
Section titled “Failures that produce no error”These are the expensive ones, because nothing tells you.
| What you wrote | What happens |
|---|---|
| rules with equal salience | the firing order differs from run to run. Give every rule in an ordered set its own salience |
the same statement twice in a row in one then | the second does not run: the engine reuses the first evaluation. Write each statement once |
a malformed regex in a strings.* helper | degrades quietly: false, "", nothing, or the input unchanged |
an unknown operation name in bbox.PostprocessRows | the value is left as it was |
a fact named like a collector (out, in, findings, audit) under kis rules | the collector wins and your value is dropped. rules.svc refuses such a fact instead |
a fact named like a helper (map, strings, math, …) under kis rules | it replaces the helper, and the first call into it fails. rules.svc refuses such a fact instead |
audit.Log(…) on rules.svc without options.audit | the trail is collected and not returned. Ask for it with "options": {"audit": true} |
a proposal under execute | execute never writes: proposals come back as actions. Use evaluate with apply to write |
a --require-bbox input whose .bbox sidecar is missing | dropped when it came from a directory glob or a shell-expanded path, with a warning: skipped N input(s) … line on stderr. A prefix you named yourself is an error instead |