Rule Errors
Each problem in a version 2 .rules file has one error code. The tool shows the code in square brackets. Use the code and the hint to fix the rule. For the grammar, see Rule Syntax.
Error format
<file>:<line>[:<col>]: rule "<id>": <message> [<CODE>]. Hint: <how to fix it>
Example:
.sigmashake/rules/egress.rules:9: rule "egress-example": a rule with ELSE IF or ELSE needs END before } [V2_MISSING_END]. Hint: write END on its own line after the last branch
The rule "<id>": part is absent for a problem outside a rule, such as a data block.
What happens to an invalid rule
An invalid rule is not loaded. The rest of the file still loads. Each skipped rule or data block has one reason. The reason shows in these places:
ssg lint, andssg lint --jsonfor a machine-readable formssg rule syncssg status- The Rules page of the dashboard
- The SessionStart banner
The write paths refuse an invalid rule before it reaches disk. These paths are save, inbox, import, and ssg new.
A bad data block is skipped. Each rule that uses its name then fails with V2_DATA_UNKNOWN_NAME.
Some problems skip the whole file, so the file loads no rule:
- A line outside every block, such as a stray line.
- A header that cannot be read.
- More than 64 bad blocks in one file.
When a file has a skipped rule, the DB rows of that file stay until the file is fixed. Fix the reported rule, then run ssg lint again.
Codes for files and rules
| Code | Meaning | Fix |
|---|---|---|
RULE_SYNTAX | A line does not match the grammar. The code also covers the CONTEXT, CATEGORY, TAG and severity checks, and any error with no exact code. | Read the message and the hint. Check the spelling of the keyword. |
V2_EXPECTED_RULE | A line at file scope is neither a rule nor a data block. | Start a rule with rule <id> {. Start a data block with set, domains, prefixes or map. |
V2_UNTERMINATED_RULE | A rule has no closing }. | Add } after the last line of the rule. |
V2_AMBIENT_HAS_CONDITION | An AMBIENT rule has an IF or OR condition. | Remove the conditions, or remove AMBIENT. Ambient rules never gate a tool call. |
V2_DUPLICATE_DIRECTIVE | A directive appears twice in one branch. | Keep one copy in each branch. A branch ends at ELSE IF, ELSE or END. |
V2_TOO_COMPLEX | The rule expands to too many OR and AND groups. The file has more than 8192 groups or 65536 conditions in total, shared by all its rules. The rules of the file reference more than 16 MiB of data, because each rule counts a copy of each data block that it uses. | Split the rule, or use fewer OR and AND groups. For the data limit, use fewer rules with a big data block, or a smaller block. |
V2_NESTING_TOO_DEEP | The condition has more than 16 levels of parentheses. | Flatten the condition, or split the rule. |
Codes for regex patterns
These codes apply to the pattern of each REGEX and LINE_REGEX condition, in version 1 files and in version 2 files.
| Code | Kind | Meaning | Fix |
|---|---|---|---|
RULE_REGEX_REJECTED | Error | The regex engine cannot run the pattern. The message shows the reason from the engine. A lookbehind such as (?<=a)b, an atomic group, an inline flag such as (?i), a pattern of more than 500 bytes, and a group that has a quantifier inside it and a quantifier after it are examples. The rule fails secure: a block rule always fires, and an allow rule never fires. | Write the pattern in the syntax that the engine reads. Shorten it. Use (?= or (?! for a lookahead. |
RULE_REGEX_QUANTIFIED_ALTERNATION | Error or warning | A quantifier follows a group that has an alternation, for example (a|b)+. The flat form matches every input. A nested form is a warning in version 1 files, and an error in version 2 files. | Write the alternatives as separate IF and OR conditions. |
RULE_REGEX_DOUBLED_QUANTIFIER | Warning | A repeat operator follows another repeat operator, for example a++ or a**. The engine reads the second operator as a literal character. The rule still loads. | Write \+ or \* if you mean the character itself. If you do not, remove the second operator. |
Codes for data blocks
| Code | Meaning | Fix |
|---|---|---|
V2_DATA_SYNTAX | A data block has a syntax error. The block has no {, is not closed, has an unclosed string, has text after }, or uses = outside a map. | Write set NAME { "entry" }. Close the block with }. Write a map entry as "key" = "value". |
V2_DATA_BAD_NAME | The data name does not match the name rule. | Start the name with a lowercase letter. Use lowercase letters, digits and _ only. |
V2_DATA_REDECLARED | A data name is declared twice in one file. | Keep one block for each name. Or choose another name. |
V2_DATA_EMPTY | A data block has no entries. | Add an entry, or delete the block. |
V2_DATA_TOO_LARGE | A data block has more than 4096 entries, or the data blocks of one file hold more than 2 MiB of entry text. | Split the data. For a long list, use a threat feed. For the 2 MiB limit, remove entries, or keep the long list outside the rules file. |
V2_DATA_TOO_MANY_BLOCKS | A file has more than 64 data blocks. | Merge the blocks, or split the file. |
V2_DATA_BAD_ENTRY | An entry fails the check for its kind, or it is longer than 512 bytes. | Follow the hint. A domain is a plain host name. A prefix is an absolute path. Other entries are non-empty strings of at most 512 bytes. |
V2_DATA_DUPLICATE_ENTRY | An entry appears twice after it is normalized. | Delete the second entry. |
V2_DATA_UNKNOWN_NAME | A condition uses @name, and no block above the rule declares that name. | Declare the block above the rule. |
V2_DATA_KIND_MISMATCH | The operator needs a different kind of block. | Use set for IN, domains for HOST_IN, prefixes for UNDER and map for MAP. |
Codes for branches and decisions
| Code | Meaning | Fix |
|---|---|---|
V2_TOO_MANY_BRANCHES | A rule has more than 16 branches. | Use a map for a long table, or split the rule. |
V2_ELSE_WITHOUT_IF | An ELSE or END has no IF above it. | Write IF <condition> first. |
V2_BRANCH_AFTER_ELSE | A branch follows ELSE. | Make ELSE the last branch. Put ELSE IF branches above it. |
V2_IF_IN_LATER_BRANCH | An IF appears after the first branch. | Start the next branch with ELSE IF <condition>. |
V2_BRANCH_NO_CONDITION | A branch that is not ELSE has no condition. | Start the branch with IF <condition>. |
V2_BRANCH_NO_DECISION | A rule with several branches has a branch with no decision line. | Add a decision line, such as DENY execution. |
V2_MISSING_END | A rule has ELSE IF or ELSE, and no END before }. | Write END on its own line after the last branch. |
V2_AFTER_END | A line follows END and is not }. | Move the line above END, or into a new rule. |
V2_MISSING_MESSAGE | A branch or a rule has no MESSAGE. | Add MESSAGE "text". |
V2_MISSING_PROMPT | An ASK branch has no PROMPT. | Add PROMPT "question". |
V2_MISSING_SUBSTITUTE | A FORCE branch has no SUBSTITUTE. | Add SUBSTITUTE "command". |
V2_MAP_OPERATOR_INVALID | A data operator follows MAP. This includes OWNED_EGRESS and OWNED_EGRESS_PIPE_SHELL. | Use a content operator after MAP, such as EQUALS, STARTS_WITH, CONTAINS or REGEX. |