Skip to content
Talk to our solutions team

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.

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:

StatusBodyWhy
401Unauthorizedno token, or one that does not verify
400invalid tenantthe tenant headers are missing or do not match the token
404emptyno such route
CodeStatusWhen
rules-json-request-parse-failed400the body is not JSON, or holds a key the route does not read (json: unknown field "audits")
rules-body-too-large413the body is over rules.body.maxbytes
rules-name-missing400no scope (or no name on POST /rule)
rules-facts-invalid400a 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-served400the tenant is not in rules.svc’s configuration
rules-tenant-not-loaded503the tenant’s rules have not loaded yet. Comes with Retry-After
rules-scope-not-found404no static scope and no top folder of that name
rules-access-denied403the caller may not do this (Access)
rules-execution-failed422a 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-timeout504the execution ran past its timeout
rules-source-unavailable503a source the scope reads could not be reached
rules-source-denied403a source refused the caller
rules-write-not-declared422a proposal the scope’s writes do not allow (a record’s error, in evaluate and sweeps)
rules-write-conflict409a record changed while it was being decided, twice (a record’s error)
rules-write-partial207a record’s writes applied in part (a record’s error)
rules-sweep-not-found404no sweep of that id that the caller may see
rules-sweep-limit429the tenant has rules.sweeps.pertenant sweeps queued or running
rules-sweeps-disabled409sweeps 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"}}}}

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.

MessageCause
<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 Ma wrong number of arguments
<file>: got N error(s) in grl the scriptthe 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.writesa 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:

WrittenWrite 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 whenone expression, joined with && or ||
a then statement without its ;every statement ends in ;, the last one too

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.

MessageCause
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 objectdot access on a map fact (order.Total). Use in.Double("order.Total", 0.0)
reflect: Call using int64 as type float64a whole-number literal passed to a float parameter: write 10.0, not 10
reflect: Call using int64 as type intthe 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 Valuea 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 mapa 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 reachedindex 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 supporteda 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 cyclesa rule calling agenda.Repeat() whose when stays true: the cycle cap stopped it
execution timed outthe 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.

Everything below exits 1 and writes to stderr unless stated otherwise. A run ended by a signal exits with 128 plus the signal number.

MessageCause
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) failedthe 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.

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.

MessageCause
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" objectan 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 headerskis bbox table -H found a table with no header row
missing headers: <a> (found: <b>)header validation failed against the detected table
LimitDefaultSet byWhen it trips
Execution time10srules.exec.timeout, a static scope’s limits.timeout (capped by rules.exec.maxtimeout, 30s)504 rules-execution-timeout
Cycles5000rules.exec.maxcycles, a static scope’s limits.maxcycles422 rules-execution-failed, with the partial output
Request body1 MiBrules.body.maxbytes413 rules-body-too-large
Named facts per call20—400 rules-facts-invalid
Records per evaluate100rules.evaluate.maxids400 rules-facts-invalid
Knowledge rows1000, or the read’s maxthe scope422 rules-execution-failed
A watched run10s, 2000 cycles; a draft at most 200 files and 4 MiBfixedthe run’s error
Sweeps per tenant1 at a timerules.sweeps.pertenant429 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.

These are the expensive ones, because nothing tells you.

What you wroteWhat happens
rules with equal saliencethe 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 thenthe second does not run: the engine reuses the first evaluation. Write each statement once
a malformed regex in a strings.* helperdegrades quietly: false, "", nothing, or the input unchanged
an unknown operation name in bbox.PostprocessRowsthe value is left as it was
a fact named like a collector (out, in, findings, audit) under kis rulesthe 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 rulesit replaces the helper, and the first call into it fails. rules.svc refuses such a fact instead
audit.Log(…) on rules.svc without options.auditthe trail is collected and not returned. Ask for it with "options": {"audit": true}
a proposal under executeexecute never writes: proposals come back as actions. Use evaluate with apply to write
a --require-bbox input whose .bbox sidecar is missingdropped 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