Diagnostics
Every problem Clarive HCL reports carries a stable code. Codes are append-only: a retired code is never recycled for a different meaning.
cla-objects/prod/generic_server.web01.hcl:4:11: error[HCL017]: Bare identifier 'bastion' used as a value -- did you mean the string "bastion" or a reference like resource.<class>.<name>?
4 | proxy = bastion
| ^
file:line:column: severity[CODE]: message, then the source line and a caret.
An item with no position renders as file: severity[CODE]: message. Tabs in
the context line become single spaces so the caret lands under the right
column.
Two severities: error and warning. Errors refuse whatever was being done;
warnings are reported and the work continues. A run collects at most 200 items
so a pathological file cannot spin out megabytes of output.
Codes are grouped by the stage that raises them.
| Range | Stage | Raised by |
|---|---|---|
| HCL0xx | lexer and parser | reading the text |
| HCL1xx | encoder | cla export, the UI encode endpoints |
| HCL2xx | decoder, catalog, resolver, plan, apply | cla import, import-plan, diff, the UI save endpoints |
| HCL3xx | rules | anywhere a rule block is read or written |
Lexer — HCL001 to HCL005¶
| Code | Severity | Message |
|---|---|---|
| HCL001 | error | Unexpected character %1 |
Unexpected character %1 -- HCL strings use double quotes (for ') |
||
Unexpected character %1 -- HCL has no backtick strings, use a heredoc (for `) |
||
Unexpected character %1 -- HCL statements end at the end of the line (for ;) |
||
Unexpected character %1 -- only meaningful inside a quoted string or heredoc (for $, @, \, &) |
||
| HCL002 | error | Unterminated string, a quoted string cannot span lines |
Unterminated string, missing closing %1 |
||
| HCL003 | error | Unterminated heredoc, missing closing %1 |
| HCL004 | error | Unterminated block comment, missing %1 |
| HCL005 | error | Invalid escape sequence %1 in string |
On an unexpected character the lexer reports once per line and resyncs to the end of that line.
Parser — HCL010 to HCL020¶
| Code | Severity | Message |
|---|---|---|
| HCL010 | error | Block %1 has %2 labels, a block takes at most two |
| HCL011 | error | Block label %1 must be a quoted string |
| HCL012 | error | Expected %1 to open the body of block %2, found %3 |
| HCL013 | error | Unclosed block %1 opened at line %2, expected %3 |
| HCL014 | error | Unexpected %1 at the top level of the file |
Unexpected %1, expected an attribute or a block |
||
Unexpected %1, expected a value |
||
Expected an identifier after %1, found %2 |
||
Unclosed call to %1, expected %2 |
||
Expected %1 or %2 in the arguments of %3, found %4 |
||
Expected %1 or %2 in list, found %3 |
||
| HCL015 | error | Expected %1 or a block body after %2, found %3 |
| HCL016 | error | Expected end of line after the value of %1, found %2 |
Expected end of line after block %1, found %2 |
||
| HCL017 | error | Bare identifier %1 used as a value -- did you mean the string %2 or a reference like %3? |
| HCL018 | error | Unclosed list opened at line %1, expected %2 |
| HCL019 | error | Unclosed object opened at line %1, expected %2 |
Expected an object key, found %1 |
||
Expected %1 or %2 after object key %3, found %4 |
||
Expected %1 or %2 in object, found %3 |
||
| HCL020 | error | Unsupported HCL construct: conditional expression -- Clarive HCL v1 does not evaluate expressions |
Unsupported HCL construct: operator %1 -- ... |
||
Unsupported HCL construct: unary operator %1 -- ... |
||
Unsupported HCL construct: parenthesised expression -- ... |
||
Unsupported HCL construct: %1 expression -- ... (for) |
||
Unsupported HCL construct: index access -- reference Clarive objects by address instead |
||
Unsupported HCL construct: splat expression -- list every reference explicitly |
||
| HCL020 | error | Unsupported HCL construct: %1 block -- Clarive HCL v1 does not generate blocks (dynamic) |
Every parser diagnostic is an error. HCL010 and the dynamic form of HCL020
are the two that do not abort the construct being parsed — the offending labels
or block are kept in the tree and parsing continues — but they still refuse the
run like any other error.
The parser resyncs to the next newline at the current brace depth, so an error inside one block is contained to that block. Any parse error makes the whole tree untrustworthy — nothing downstream of one is ever written.
See Syntax for the complete list of refused constructs.
Encoder — HCL100 to HCL112¶
| Code | Severity | Message |
|---|---|---|
| HCL100 | warning | Skipped an object of family %1 because it has no name to build a label from |
| HCL101 | warning | No encoder for family %1 yet |
| HCL102 | warning | Dropped a reference to %1 because it could not be resolved |
| HCL103 | warning | Dropped a reference to %1 because class %2 is internal |
| HCL104 | error | Cannot encrypt secrets: no cipher available. Use %1 instead |
| HCL105 | — | reserved; defined but never raised |
| HCL106 | warning | Refused to write %1 for %2: it is a database id and no address could be found for it |
Refused to write %1 for %2: it is a list of database ids and they will not mean the same thing elsewhere |
||
| HCL107 | warning | The value of %1 has %2 written inside it. That is a database id and it will not mean the same thing elsewhere |
| HCL110 | warning | Skipped the topic %1 because its category could not be named |
| HCL111 | warning | Field %1 is stored on topics of %2 but the category form does not define it, so it is written as raw |
| HCL112 | warning | Dropped a reference to topic %1 because its category is not in %2 |
HCL104 is the only encoder error and it exits cla export with 2. It fires
only when the selection actually contains a sensitive attribute — an
encrypt-mode export of a secret-free selection exits 0 silently.
HCL106 means an attribute was not written. Read the export summary: a grant that lost a bound this way is narrower on the target than it was on the source.
HCL107 is the one leak the id gate cannot close. A mid written inside a Perl body, a saved JSON condition or a YAML stash is one opaque string to the encoder. Warning is the honest answer: refusing would delete somebody's code over a substring, and rewriting it would be a guess the import could not undo. Reported once per distinct id per export.
Decoder — HCL200 to HCL219, HCL222 and HCL223¶
| Code | Severity | Message |
|---|---|---|
| HCL200 | error | Unknown block type %1 |
| HCL201 | error | Block type %1 is not supported yet |
| HCL202 | error | Block %1 needs a name label |
| HCL203 | error | A resource block needs a class label and a name label |
| HCL204 | warning | Ignored %1 on a user block: imports never touch credentials |
| HCL205 | warning | Attribute %1 is not declared by %2 and will be stored as is |
| HCL206 | error | Attribute %1 expects references, not a literal |
| HCL207 | error | Unknown function %1 |
| HCL208 | error | Function %1 needs one argument |
| HCL209 | error | Cannot decode %1: no secret key given. Pass %2 |
| HCL210 | error | Cannot decode %1: wrong key, or the value is not encrypted |
| HCL211 | error | A %1 block needs a %2 attribute (moved, to) |
| HCL212 | error | A %1 block needs an %2 attribute (removed, at) |
| HCL213 | error | Block %1 is not allowed inside %2, only rules hold blocks like this |
| HCL214 | error | Write this as %1, not as a resource -- it is not administered like one |
| HCL215 | error | A topic block needs a category label and a name label |
| HCL216 | error | Attribute %1 does not belong at the top of a topic block, put it in %2 |
| HCL217 | error | Block %1 is not allowed inside a topic, only %2 and %3 are |
| HCL218 | error | %1 must be a map, like %2 -- variables, one of its environments, a _natures, or a nature override (Variables) |
| HCL219 | warning | Ignored the grants of user %1: a user in groups takes its grants from them |
| HCL222 | error | Write group membership on each user, as %1, not as %2 on the group |
| HCL223 | error | Attribute %1 lists user groups, and %2 is not one |
HCL222 and HCL223 sit outside the decoder's range because HCL220 and HCL221 were already taken by the catalog, and codes are never renumbered once they ship. HCL219, HCL222 and HCL223 are all about who holds a user's permissions.
HCL205 is silenced entirely by --lax, for files written by an installation
with attributes this one has never heard of. Unknown blocks are still
refused.
HCL205 and HCL213 are not raised for role, user or group blocks: those
families decode without a schema check, so an unknown attribute is accepted and
then quietly discarded by the apply step. See
Roles, users and groups.
Catalog, resolver, plan, apply — HCL220 to HCL253¶
| Code | Severity | Message |
|---|---|---|
| HCL220 | error | Cannot read %1: %2 |
| HCL221 | error | Duplicate address %1, already declared at %2 |
| HCL230 | error, or warning under --allow-missing |
Unresolved reference %1 in %2 |
| HCL240 | warning | Nothing to remove at %1, it is already gone |
| HCL250 | error | Refusing to apply: the plan has conflicts or errors |
| HCL251 | error | Failed to apply %1: %2 |
| HCL252 | error | Failed to link references for %1: %2 |
| HCL253 | error | Failed to delete %1: %2 |
HCL221 usually means the same object was declared twice, often once in each spelling. The first declaration wins.
Under --allow-missing, HCL230 becomes a warning and the depending object is
marked SKIPPED. The skip is contagious.
HCL251, HCL252 and HCL253 are apply failures. They exit 3, and the run reports exactly what was applied and what was not. There is no rollback.
Rules — HCL300 to HCL321¶
| Code | Severity | Message |
|---|---|---|
| HCL300 | warning | Skipped a rule node with no operation key |
| HCL301 | error | Not a rule block: %1 |
| HCL302 | error | A rule block needs a name label |
| HCL303 | warning | Unknown rule attribute %1 |
| HCL304 | error | Unknown rule operation %1 |
| HCL305 | error | Cannot read the condition %1 |
| HCL306 | error | Unknown function %1 |
| HCL307 | error | Function %1 needs one argument |
| HCL308 | error | Cannot decode %1: no secret key given. Pass %2 |
| HCL309 | error | Cannot decode %1: wrong key, or the value is not encrypted |
| HCL310 | error | No rule block found in this file |
| HCL311 | error | Write exactly one step here |
| HCL312 | error | Nested steps are edited in the rule tree |
| HCL313 | error | The operation cannot change here: this step is %1 |
| HCL320 | error | the Perl compiler's own message, on the offending line |
| HCL321 | error | This step does not compile: %1 |
HCL306 to HCL309 are the rule-body mirror of HCL207 to HCL210.
HCL310 is only raised by the UI rule editor, where a file with no rule block in it is a mistake rather than an empty file.
HCL311 to HCL313 are raised only by the HCL tab of the Rule Designer's
operation dialog, which edits one step on its own. The tab holds exactly one
block and no top-level attributes (HCL311). The step's children stay in the
rule tree, so a nested block is refused rather than silently dropped (HCL312).
The operation itself is fixed by the node being edited (HCL313): the text may
pick another form of the same operation, such as sh running locally or
remotely, but not turn an if into a foreach.
HCL320 is the embedded-Perl syntax check. The parser treats a perl/perl_code
body (or a perl_for, and a foreach with in_perl) as opaque text, so the UI rule editor
compiles each Perl block on its own and reports the compiler's real message on
the HCL line that holds it. It is raised only for a caller that may modify rules
— compiling Perl runs its BEGIN/use, which is a write-level capability, not a
read-only one — so a view-only validate never triggers it.
HCL321 comes from saving a rule in the UI rule editor. A text can parse, decode and pass HCL320 and still make a rule that does not compile — an IF condition's Perl, for one, is not a body HCL320 compiles. The rule compiler then refuses the save, and Perl counts its line numbers in the code generated from the whole rule, which is nowhere on screen. So the compile is repeated with every step marked with the HCL line it was written on, and each error is reported on the line of the step it came from, in Perl's own words. The compiler's full message is kept beside it.
HCL304 is worth singling out: the node is dropped and the rest of the rule imports. The most common cause is not a typo but the keyword inversion gap described in Rules.
Diagnostics::code_title does not know every code
The in-code catalogue covers HCL000–HCL107 and HCL300–HCL313. The
HCL110–HCL112 and HCL200–HCL253 ranges ship and are emitted, but are absent
from that table, so code_title returns nothing for them and codes()
omits them. The messages themselves are unaffected; only a caller
enumerating codes programmatically will see the gap.
HCL000¶
The fallback when a caller raises a diagnostic without a code. It has no title. Seeing it means a code was forgotten at the call site.