Profiles
Profile diagnostics
Every E_, W_ and I_ code the resolver, the consent generator and the formatters return.
The resolver and the validator report problems as a list of diagnostics; the formatters, the consent generator and
the terms functions throw an error that carries a code. Either way, match on the code: the message is written
for a person or a model to read. Codes are grouped below by the package that returns them,
with the spec's §13 table first. Every code here is copied from
spec §13 or from the
package's source under profile/packages.
export interface Diagnostic {
severity: "error" | "warning" | "info";
code: string;
/** A JSON pointer for E_SCHEMA, an address (`send(0).to`, `type:Tokens.e8s`) or a document path (`binds.interface_id`). */
at: string;
message: string;
/** The profile's name, when the diagnostic comes from one. */
profile?: string;
}| Where | How a failure arrives | Codes |
|---|---|---|
@meantjs/profile (resolveService, validateProfile, the candid-profile CLI) | diagnostics: Diagnostic[]; ok is false when one is an error | §13, doc annotations |
@meantjs/profile/evm | throws AbiError with code | AbiError |
@meantjs/format | throws FormatError with code; safeParse and safeDisplay return it | FormatError |
@meantjs/consent and its subpaths | throws ConsentError; icrc21ConsentMessage maps it to an ICRC-21 error; adjustments go to diagnostics | ConsentError, diagnostics |
@meantjs/consent/terms | throws TermsError with code for a term the agent wrote that cannot be read, or a step out of order; ConsentError EE_REQUEST for input not in the shape the functions write | TermsError |
@meantjs/tools | readToolInput returns { issues } | input issues |
@meantjs/reconcile | no codes: matchBlocks returns a status | statuses |
The candid-profile CLI exits with 0 when no diagnostic is an error, 1 when one is (the resolved form is
still printed when the service compiles), and 2 for a usage or I/O problem
(The candid-profile command).
Resolver and validator: spec §13#
Returned by @meantjs/profile. Rows that §13 groups (E_ADDR_SYNTAX / E_ADDR_UNRESOLVED) are
split here, one code a row, with the meaning that half of the §13 row gives.
Schema, interface and input#
| Code | Severity | Meaning |
|---|---|---|
| EE_SCHEMA | error | Fails the JSON Schema. at is a JSON pointer. |
| EE_CANDID | error | The interface (requires or --did) does not compile. |
| EE_ENVELOPE | error | The interface was given as a candid-core ContractEnvelope (--envelope) that the resolver cannot read: not an object, no contract, a contract.format other than candid-core, a format_version other than 1, no contract.identities.interface, a type reference outside contract.types, or any other failure of candid-core's own structural or canonical checks. No resolved form is produced. |
| EE_SERVED | error | A profile a canister serves (§8.1) that cannot be used: its bytes are not UTF-8 JSON (or start with a byte order mark), it is over 1 MiB, it is not one JSON object of kind: "canister" and origin: "author", or its binds.canister_id names another canister. The profile is not applied; the rest of the service resolves without it. |
| EE_ABI | error | An EVM ABI (binds.abi, or the contract's given as the interface) that cannot be read: a fragment that does not parse, a type that does not exist, an overloaded function name (O1), two components with one field id, or no function at all (§15.1). |
| EE_CHAIN | error | A profile or pack for an EVM contract given for a Candid service, or the other way round (§15.3). |
| EE_ADDRESS_CHECKSUM | error | A deployment address in mixed case that does not match its EIP-55 checksum (§15.3). |
| EE_EIP712 | error | binds.eip712.types that cannot be read (a struct name that is not an identifier or reads as an ABI type or EIP712Domain, a member type that is not canonical or not a type, a struct that contains itself or refers to a refused one), a message that shares its name with a function, two profiles that give one type name different members, or types with no message and no function (§15.9). Experimental. |
| WW_NO_INTERFACE | warning | Canister profile checked without its .did; semantic checks skipped. |
Addresses and tags#
| Code | Severity | Meaning |
|---|---|---|
| EE_ADDR_SYNTAX | error | Address does not parse. |
| EE_ADDR_UNRESOLVED | error | Address does not resolve. |
| EE_PACK_TYPE_REF | error | type: address in a pack. |
| EE_TYPE_NODE | error | Pathless type: address on a declaration that is not a record or a variant. |
| EE_TAG_TARGET | error | Wrong kind of target. Includes an intent or effect at a value address that is not a case of a variant in the arguments, or that steps into [] (§5.2), and on an EVM contract idempotency, outcome, list, names and named (not yet) (§15.4). |
| EE_TAG_NOT_APPLICABLE | error | Wrong Candid type. Includes account and subaccount on an EVM contract (no ABI type) (§15.4). |
| EE_REF_UNRESOLVED | error | Bad dynamic value: the query: method is not in the interface. |
| EE_REF_NOT_QUERY | error | Bad dynamic value: the query: method is not a query. |
| EE_REF_SHAPE | error | Bad dynamic value: the method takes arguments, or does not return exactly one value. |
| EE_REF_TYPE | error | Bad dynamic value: the method returns the wrong kind (a number expected, or text). |
| EE_DYNAMIC_KIND | error | Bad dynamic value: now or caller where only query: is valid. |
| EE_DEFAULT_NULL | error | null default on a non-optional position. |
| EE_TOKEN_PATH | error | An amount.token that is not a relative path to an address in the same argument and the same array element as the amount, or that is given on a Candid service (§15.9, OQ-6). Experimental. |
| WW_STRUCTURAL_ALIAS | warning | A type: address starts from a declaration whose type another declaration also names; the tag reaches both (§3.2 rule 7). |
| II_UNTAGGED_NUMERIC | info | A numeric update argument has no meaning attached. |
Methods: effect, idempotency, outcome, templates#
| Code | Severity | Meaning |
|---|---|---|
| EE_EFFECT_MODE | error | An effect that does not fit the method: a query or composite query declared as something other than read, or replicated: true anywhere but on an update method with kind: "read" and irreversible: false (§5.3). |
| WW_EFFECT_MODE | warning | A read on an update or a oneway method, except the read-only update form (§5.3). |
| EE_CASE_OVERLAP | error | Two tagged cases of one method, for the same tag, that one value can take both (§5.2). |
| EE_CASE_EFFECT | error | A case effect that needs confirmation, or is irreversible, under a method effect that does not or is not (§5.3). |
| EE_GRANT | error | A grant on an effect that is not a method's transfer or approve (another kind, a case effect), an approve's grant with to, or a position that breaks §5.3: a path that does not resolve, steps into [] or passes through a case of a variant; a spender or to that is neither an address nor account-tagged; an amount that is neither an unsigned amount-tagged position nor a bool; an optional spender, to or amount; an ends position without a timestamp tag (§5.3, D-056). In a pack applied to a service, the grant is dropped instead (II_PACK_ANNOTATION_SKIPPED, below). |
| EE_IDEMPOTENCY_MODE | error | Idempotency on a query. |
| EE_STAMP_UNTAGGED | error | Stamp without a timestamp tag. |
| WW_STAMP_REQUIRED | warning | The stamp field is not optional; the tool layer must always fill it. |
| EE_OUTCOME_NOT_CASE | error | Outcome key is not a variant case. |
| WW_OUTCOME_UNCLASSIFIED | warning, or info when default is set | A result case without a class. |
| EE_TEMPLATE | error | Template syntax, or a [ ] segment on an EVM contract (§15.4). |
| EE_TEMPLATE_PATH | error | Unknown placeholder, or one through another case of a variant on a case intent's path (§6). |
| WW_TEMPLATE_OPTIONAL | warning | A placeholder that resolves to an opt position outside [ … ] (§6). |
Names#
| Code | Severity | Meaning |
|---|---|---|
| EE_NAMES_MODE | error | A names set on a method that is not a query or a read-only update. |
| EE_NAMES_SHAPE | error | A names set on a method that takes arguments, or whose items is not a vec of records. |
| EE_NAMES_FIELD | error | A names set whose name is not text. |
| EE_NAMES_SET_DUPLICATE | error | A names set declared twice. |
| EE_NAMED_SET | error | A named position whose set is not declared (or has no show for by: "show"). |
| EE_NAMED_TYPE | error | A named position whose type differs from the set's field. |
| WW_NAMED_UNPATTERNED | warning | A named text position without a text.pattern: names are looked up before values. |
Layers, binding and packs#
| Code | Severity | Meaning |
|---|---|---|
| EE_CONFLICT | error | Two different values for one tag at one position (within one layer, at one specificity; the first one given is kept). |
| WW_DUPLICATE | warning | Equal values for one tag at one position and specificity (§8); a deployment listed twice (§15.3). |
| WW_OVERRIDDEN | warning | The same tag at the same position in a lower layer, dropped (§8). |
| WW_REBOUND | warning | Interface changed; reported first, then either confirmed or followed by EE_STALE. |
| EE_STALE | error | Bound interface changed and some address or tag no longer holds. |
| EE_PACK_NOT_MATCHED | error | A pack the caller asked for explicitly does not apply to the service. |
| WW_PACK_NO_DEFAULT | warning | A pack outcome tag without a default (§7). |
| II_PACK_MATCHED | info | A pack applies; the message lists every result variant wider than the fragment (D-019) and its extra cases. |
| II_PACK_NOT_MATCHED | info | A candidate pack from the pack search does not apply; the message gives the first reasons. |
| II_PACK_ANNOTATION_SKIPPED | info | A matched pack names a position the service lacks, which subtyping allows; the annotation is dropped. Since 0.4.0 also a pack's grant that does not hold in the service it is applied to: the grant is dropped and the effect kept ("effect grant dropped, the effect kept: E_GRANT: …"; §5.3). |
A stale canister profile loses its value-conversion tags in the resolved form and keeps the rest; see
Profiles, layers and binding. The codes that make a profile stale when they appear after
a rebind are listed in STALE_CODES in
checks.mjs.
Resolver: doc annotations#
Returned by profileFromDocAttributes when it turns the tags written as doc comments in a
.did into an author profile (spec §9, Annotate a .did file). Not in
§13. On an error the profile is still returned with what could be placed. Meanings are those of the package's
README and src/doc-annotations.mjs.
| Code | Severity | Meaning |
|---|---|---|
| EE_DOC_INPUT | error | Not a doc attributes v1 response. |
| EE_DOC_SYNTAX | error | candid-core refused the comment. candid-core's compile failures pass through as EE_CANDID. |
| EE_DOC_TARGET | error | A tag no v0 address reaches, such as inside a func value or a callback service, or one in the wrong place. |
| EE_DOC_VALUE | error | A value that cannot be read: a fraction, an unknown call. |
| WW_DOC_UNKNOWN_TAG | warning | A bare name one or two letters from a tag (@amout): not a profile tag. |
| II_DOC_IGNORED | info | The doc comments hold no profile tag, so there is no profile; or a name that is not a profile tag (a dotted name such as @ui.hidden belongs to another tool). |
| II_DOC_PROSE | info | Prose not attached as describe.help: on an integer the same doc gives no value tag, prose would make it an agent input without a unit (D-044). |
@meantjs/profile/evm: AbiError#
The EVM helpers throw AbiError (name: "AbiError", code). Experimental, spec §15;
see EVM contracts.
| Code | Severity | Meaning |
|---|---|---|
| EE_ABI | error, thrown | A fragment that cannot be read (parseSignature). parseAbi lists its problems instead of throwing. |
| EE_ABI_ENCODE | error, thrown | A value encodeParams, encodeCall or typedDataDigest cannot encode: an integer that is not a decimal string or a bigint, or outside its type; the wrong number of array elements; a missing or unknown tuple component; text that is not well formed. |
| EE_ABI_DECODE | error, thrown | decodeParams: the data is not the canonical ABI encoding of its values (dirty padding, a non-canonical offset or trailing bytes), ends early, or is over the limits. |
| EE_ADDRESS | error, thrown | Not an address (0x and 40 hex digits). |
| EE_ADDRESS_CHECKSUM | error, thrown | readAddress: an address in mixed case that does not match its EIP-55 checksum. |
| EE_EIP712 | error, thrown | encodeType and eip712TypeHash: a primary type that is not in the types (§15.9). parseEip712Types lists its problems instead of throwing; the resolver reports them as the EE_EIP712 diagnostic above. |
@meantjs/format: FormatError#
Every rejection of the format library is a FormatError with a stable code, one of
CODES. The meanings are the comments in
src/errors.mjs
where it has one, and otherwise the messages the code is thrown with. See
Parse and display values.
| Code | Group | Meaning |
|---|---|---|
| EE_TYPE | shared | Wrong JavaScript type for an input, for example a number where a decimal string is required. |
| EE_SYNTAX | shared | The text does not match the grammar. |
| EE_RANGE | shared | Outside the range of the Candid type or of the representable calendar. |
| EE_TOO_LONG | shared | Input longer than the bound on work. |
| EE_ARG | shared | A tag argument is missing or invalid. |
| EE_TAG_TYPE | shared | The tag does not apply to this Candid type. |
| EE_TAG_CONFLICT | shared | Two formatting tags at one position. |
| EE_REF_UNRESOLVED | shared | A dynamic value was not supplied by the caller. |
| EE_EXCESS_PRECISION | amount | More fractional digits than decimals (never rounded). |
| EE_NEGATIVE | amount | Negative input for an unsigned Candid type. |
| EE_SYMBOL_MISMATCH | amount | The symbol in the input is not the token's symbol. |
| EE_INEXACT | time | The value cannot be represented exactly in the requested unit. |
| EE_CALENDAR_UNIT | time | Years and months have no fixed length. |
| EE_NEEDS_NOW | time | Relative time without a now. |
| EE_CHECKSUM | account, subaccount, bytes | Checksum does not match. |
| EE_CHECKSUM_MISSING | account, subaccount, bytes | An account text with a subaccount but no checksum (the form is <principal>-<checksum>.<subaccount>). |
| EE_NOT_CANONICAL | account, subaccount, bytes | A valid value written in a non-canonical way. |
| EE_PRINCIPAL | account, subaccount, bytes | Invalid principal text. |
| EE_HEX | account, subaccount, bytes | Text that is not hexadecimal. |
| EE_BASE64 | account, subaccount, bytes | Not base64: outside the standard alphabet, an impossible length, wrong = padding, or unused trailing bits that are not zero. |
| EE_UTF8 | account, subaccount, bytes | Text that cannot be encoded as UTF-8 (lone surrogate). |
| EE_LENGTH | account, subaccount, bytes | Wrong length in bytes. |
| EE_MAX_LEN | account, subaccount, bytes | Longer than the tag's max_len. |
formatterFor throws EE_REF_UNRESOLVED, EE_ARG, EE_TAG_TYPE or
EE_TAG_CONFLICT rather than return a formatter that half works. Since 0.3.0 that includes an
amount whose token names its token by a field when the caller passes no
token (EE_REF_UNRESOLVED), and a timestamp threshold without its
message, or the other way round (EE_ARG: "a timestamp threshold and its message go together:
give both or neither"). The Rust crate meant-format
returns eight of these codes for the amount tag, matched case by case against this package:
meant-format (Rust).
@meantjs/consent: ConsentError#
Every failure of the consent package is a ConsentError with a stable code.
icrc21ConsentMessage maps it to an ICRC-21 error variant whose description starts with the
code; the lower-level functions throw it. Meanings are the comments in
src/errors.mjs.
See Generate consent messages.
| Code | ICRC-21 error | Meaning |
|---|---|---|
| EE_METHOD_UNKNOWN | UnsupportedCanisterCall | The resolved form has no such method. |
| EE_DECODE | UnsupportedCanisterCall | The argument bytes do not decode against the method's signature (for an EVM call: calldata that is not the canonical encoding of its values; for an EIP-712 request: values that are not exactly the message's members, canonically written). |
| EE_TOO_LARGE | UnsupportedCanisterCall | The arguments exceed the bounds of a consent message (never truncated). |
| EE_LANGUAGE | UnsupportedCanisterCall | Only with languagePolicy: "error": the requested language is not supported. |
| EE_REQUEST | UnsupportedCanisterCall | The ICRC-21 request itself is malformed. From @meantjs/consent/evm: malformed input (the calldata, to, chainId, form or tokens, or a typedData whose shape or types cannot be read). From generateConsent: a canisterId that is not a principal's text. From @meantjs/consent/terms: a wallet, terms or grant not in the shape the functions write, terms changed after confirmTerms, or a termsGate check without a request id (below). |
| EE_DEPLOYMENT | (EVM only) | An EVM call's target is not a deployment its profile lists (spec §15). From eip712Consent: the request's verifying contract on its chain is not one the profiles list. From erc7730Descriptor: no deployment to bind the file to. |
| EE_DOMAIN | (EVM only) | An EIP-712 request's domain is for another chain, contract or name than its profile's (spec §15.9). eip712Consent also refuses a domain with a field EIP-712 does not define, a salt, no chainId, no verifyingContract, or a types.EIP712Domain that does not list the domain's fields in EIP-712 order. Experimental. |
| EE_TYPE_HASH | (EVM only) | No message of the resolved form has the request's EIP-712 type hash (spec §15.9): a request is matched by its type hash, never by its name. Experimental. |
| EE_UNSUPPORTED_TYPE | ConsentMessageUnavailable | A position the resolved form cannot type (recursive cut, func, service). On an EVM contract also a payable function, whose native value has no position yet. |
| EE_RESOLVED_FORM | GenericError 1000 | The resolved form is malformed. Also an EVM resolved form given to the main entry, and a Candid one given to @meantjs/consent/evm; for eip712Consent, a message whose encode_type does not hash to its type_hash. |
| EE_TEMPLATE | GenericError 1001 | The intent template does not parse. |
| EE_TEMPLATE_PATH | GenericError 1002 | A placeholder names a position the method does not have. |
| EE_REF_UNRESOLVED | GenericError 1100 | Pass-through of the format library: a dynamic ref the caller did not supply. On EVM also an amount in a token that is not in tokens. |
| EE_ARG | GenericError 1101 | Pass-through of the format library: a bad tag argument. |
| EE_TAG_TYPE | GenericError 1102 | Pass-through of the format library: the tag does not apply to the type. |
| EE_TAG_CONFLICT | GenericError 1103 | Pass-through of the format library: two formatting tags at one position. |
| EE_EXPORT | (EVM only) | The ERC-7730 export: the resolved form says something an ERC-7730 file cannot say (spec §15). What a file cannot say but that changes no value (a timestamp's words, a template over an array) is not refused: it is left out and listed in the export's notes. |
| EE_INTERNAL | GenericError 1999 | Not a ConsentError: any other thrown value, reported with this code in diagnostics. |
A formatter that cannot be built stops the message (rule 10 of spec §12): a number is never shown without its decimals.
@meantjs/consent: diagnostics#
What the generator adjusted, in the non-enumerable diagnostics of a result
({ code, at, message }, no severity field). The message is still produced.
| Code | Severity | Meaning |
|---|---|---|
| WW_TEMPLATE_FALLBACK | warning | A template that fails at consent time; the generic intent is used instead (spec §12 rule 12). |
| WW_FORMAT_FALLBACK | warning | A formatter refused the value; it is shown as raw Candid text (spec §12 rule 10). |
| WW_GRANT | warning | A grant that cannot be read from the request (generateConsent with a canisterId, evmConsent, eip712Consent): grant is null, at is the method or message, and the message says why, then "the request has no grant, so terms the user confirmed cannot cover it". The text is the same either way (§5.3, D-056). |
| WW_UTC_OFFSET_IGNORED | warning | utc_offset_minutes outside ±24 h; UTC is used (spec §12 rule 6). |
| II_LANGUAGE_FALLBACK | info | The requested language is not supported; the message is in "en" and the metadata says so (spec §12 rule 9). |
@meantjs/consent/terms: TermsError#
New in 0.4.0 (spec §5.3, "Confirmed terms", D-056). readTerms and termsGate throw
TermsError (name: "TermsError", code, one of TERMS_CODES). Its
message is written for the agent, which fixes the term or asks the person; readTerms never
guesses a token, an app, a number of decimals or a date. Meanings are the comments in
src/terms.d.mts,
and the messages are those of
src/terms.mjs,
with the parts in angle brackets filled in and some shortened to their last clause. See Confirm terms for a task.
| Code | Meaning and messages |
|---|---|
| EE_TERMS_MISSING | TERMS_CODES.MISSING. A field is missing or empty, or both spender and to are given. missing parameter: <field> (for the party, missing parameter: spender or to); give spender or to, not both: spender for who may spend the tokens, to for who is paid directly. |
| EE_TERMS_TOKEN | TERMS_CODES.TOKEN. A token the wallet does not know. the wallet knows no token named "<token>"; it knows <symbols>. If the user named another token, or did not say which, ask the user. |
| EE_TERMS_PARTY | TERMS_CODES.PARTY. An app the wallet does not know, or an address or account that is not one. the wallet knows no app named "<name>"; it knows <apps>. If the user named another app, or none, ask the user which app they mean, or for its address (account for an ICP token; only the apps on the token's chain are listed); <role> "<text>" is not a 0x address with 40 hex digits; an address in mixed case whose EIP-55 checksum does not match (a character may be mistyped. Ask the user to check the address); <role> must be <what>; got "<text>" when the wallet knows no app on that chain; and an app the wallet knows only on the other chain. |
| EE_TERMS_AMOUNT | TERMS_CODES.AMOUNT. An amount that cannot be read, or has more decimals than its token: never rounded. max_amount: and the parseAmount message, such as max_amount: "1.0000000000000000001" has 19 fractional digits but this token has 18; amounts are never rounded, so give at most 18. |
| EE_TERMS_END | TERMS_CODES.END. An end that cannot be read, or a date before today. expires_by must be a date as YYYY-MM-DD, a length of time from now as a number and a unit (hours, days, weeks, months or years), such as "2 hours", "30 days" or "1 month", or "never"; got "<text>"; expires_by <date> is before today, <today>; an end after the year 9999 (write "never" only if the user plainly accepts no end). |
| EE_TERMS_ORDER | TERMS_CODES.ORDER. termsGate: a step out of order. read() before the person confirmed (there are no terms yet: propose the user's terms, and read the request once the user confirms them, or the user has not confirmed the terms yet; the request can be read once they do); confirm(shown) with no draft, or with text that is not the draft's (show termsText(gate.proposed) and ask again); check before read() (read the request first: its grant is checked against the terms it was read under); a second request (these terms cover one request, the one already checked; another request is a new task, with new terms); and propose, confirm or reject once the request was read (the request was read under the confirmed terms, so they cannot be changed now; new terms start a new task). |
Input that is not in the shape these functions write is a ConsentError EE_REQUEST, not a
TermsError: a wallet without its tokens or with a now that is not a bigint, terms that did not
come from readTerms or changed after confirmTerms (terms are not the ones the
user confirmed: they changed after confirmTerms …), a grant that did not come from the consent functions, and a termsGate
check with no request id. checkGrant throws neither for a grant that does not fit: it returns
{ fits: false, reasons }, the reasons in words.
@meantjs/tools: input issues#
readToolInput returns { issues: [{ param, path, code, message }] } instead of a value when the
inputs cannot be read. The codes are the format library's, above, plus these (from the function's documentation in
src/input.mjs).
Inside a JSON input, path names the place ($args[0].command.Follow.followees[1].id). See
Give an agent tools.
| Code | Severity | Meaning |
|---|---|---|
| EE_INPUT | error | The inputs are not an object. |
| EE_UNKNOWN_PARAM | error | An input the tool does not have, or a key a JSON input does not have. |
| EE_MISSING | error | A required input is missing. |
| EE_TYPE | error | An input that is not text, or JSON of the wrong shape for a record, variant or vec input. |
| EE_NAMES_UNREAD | error | A named input whose set's names were not read. |
| EE_NAME_NOT_FOUND | error | The text is not a value at the position, and no entry of the set has that name. |
| EE_NAME_AMBIGUOUS | error | Several entries have the name: ask the person which. |
| EE_ARG | error | A tag argument the layer cannot use, such as a text.pattern it does not run. |
An EVM contract's resolved form ("chain": "evm") is refused by the tool layer with a
TypeError, not a code: EVM tools are a later phase.
@meantjs/reconcile: statuses#
@meantjs/reconcile returns no diagnostic codes. matchBlocks judges the ICRC-3 blocks read
since the first send and returns one of four statuses (Settle an unknown
outcome):
| Status | Fields | Meaning |
|---|---|---|
found | block | A block carries exactly this transaction. |
absent | closedBy: answered, too_old or window_passed; clock? | Nothing can still land: every send was answered, a send was answered TooOld, or the ledger's clock is past the stamp's window. |
not_yet | openUntil?, reason?, clock? | No match, but a send may still land. |
unreadable | reason, lookalike? | The range was not fully read, a block in it cannot be read, or a lookalike block carries the call's stamp and amount in another shape. Never absent. |