Abstract Wikipedia/Representation of errors
Motivation and rationale
editIn Wikifunctions, errors can happen for many different reasons: When an input cannot be interpreted, when a value cannot be calculated, when we don't have sufficient resources to calculate a result, and many more. Added to this, errors can also happen in a controlled way, and can be created and thrown during user-defined functions.
For this reason, Wikifunctions provides a unified way to represent and report errors with the types Z5/Error and Z50/Error type. Similarly, there is a wide variety of built-in Error type instances in the Zid range from Z500-Z599 containing the errors that are generated and returned by internal system failures or problems.
Instances of Z50/Error type
can (and probably should) be persisted, so that their labels containing the description of the error can be translated. On the other hand, instances of Z5/Error
are generated on runtime (ZObjects of type Z5 are never persisted) whenever a problem occurs. The Z5/Error
instance that is returned to the user contains: 1) a reference to the error type that more accurately describes the issue, and 2) the specific data that has generated this error.
Z5/Errors
editEvery error is a ZObject. Errors will all have the type Z5/Error, that has two keys:
- Z5K1/error type (Z50/Error Type): Takes an object of type Z50, which describes the Error type that is reported by this Z5. It will normally be a reference to one of the pre-defined Error types described below.
- Z5K2/error value (Z1/Any): An instance of the Error type described in the key Z5K1. It will contain the additional information to identify the wrong values.
Here is a simple example.
Assume we ask our validation engines to check the wellformedness of the following ZObject:
{
"value": "test"
}
|
{
"Z6K1": "test"
}
|
We should receive an error back, as the Z1K1/type is missing on the given ZObject. The exact error might depend on the evaluation engine, but some form of non-well-formedness error should be returned. Here is one possible answer.
{
"type": "Error",
"error type": "not well-formed error",
"error value": {
"type": {
"type": "Function call",
"function": "Errortype to type",
"errortype": "not well-formed error"
},
"error type": "missing Z1K1",
"error value": {
"type": {
"type": "Function call",
"function": "Errortype to type",
"errortype": "missing Z1K1"
},
"object": {
"type": "Quote",
"quotation": {
"value": "test"
}
}
}
}
}
|
{
"Z1K1": "Z5",
"Z5K1": "Z502",
"Z5K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z502"
},
"Z502K1": "Z523",
"Z502K2": {
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z523"
},
"Z523K1": {
"Z1K1": "Z99",
"Z99K1": {
"Z6K1": "test"
}
}
}
}
}
|
This shows a pretty complicated pattern, where a hierarchy of errors allows the system to filter for specific types of errors. In this instance, the hierarchy includes both Z502/Not wellformed and Z523/Missing Z1K1. We can easily recognize that this is an error because, like every error, it starts with Z5/Error as the top-level type. We'll take a closer look at Z523 in the next section.
Z50/Error types
editError types are represented by Z50, and have a very similar shape to Z4/Type objects. They have one key, Z50K1, that contains a list of the Z3/Keys necessary to describe the error.
For example, following the previous example: Z523 is a ZObject of type Z50/Error type, and its definition is:
{
"type": "Error type",
"keys": [
{
"type": "Key",
"value type": "Quote",
"key id": "Z523K1",
"label": {
"type": "Multilingual text",
"texts": [
{
"type": "Monolingual text",
"language": "English",
"text": "object"
}
]
}
}
]
}
|
{
"Z1K1": "Z50",
"Z50K1": [
{
"Z1K1": "Z3",
"Z3K1": "Z99",
"Z3K2": "Z523K1",
"Z3K3": {
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "object"
}
]
}
}
]
}
|
We see here that Z523 has a single key, with id Z523K1 and English label "object", of type Z99/Quote. The value of this key will be the (quoted) object that was found to be missing a Z1K1. (Z99/Quote is used here as a wrapper around the malformed object, to indicate that it should not be evaluated during the processing of the Z523.) This error type definition is persisted as Z523, and the key Z2K1 (Persistent object label), not shown here, contains the multilingual name of the error type: "Missing Z1K1". See the full data JSON definition.
When detecting an error of the type Z523/"Missing Z1K1", we build a Z5/Error object by:
- Adding a reference to the error type Z523 as the value of Z5K1, and
- Creating an instance of the error type Z523.
Instances of Error Types
editTo create an instance of an Error Type, we first need to transform that Error Type into a formal Type. We do this by calling to the function Z885/"Error Type to type".
This function uses the keys listed in Z50K1 to create and return a Z4/Type with those keys as its Z4K2. See the function composition Z995, which implements Z885.
An instance of an Error Type will then have, as its type, the result of the Function call to Z885, in this case passing the Error Type id Z523 as its argument Z885K1.
{
"type": {
"type": "Function call",
"function": "Errortype to type",
"errortype": "Missing Z1K1"
}
}
|
{
"Z1K1": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z523"
}
}
|
If you want to see the Z4/Type resulting from this call, try running the wikilambda_function_call
API with the following input:
{
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z523"
}
The response will be the following Z4/Type:
{
"type": "Type",
"identity": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z523"
},
"keys": [
{
"type": "Key",
"value type": "Quote",
"key id": "Z523K1",
"label": {
"type": "Multilingual text",
"texts": [
{
"type": "Monolingual text",
"language": "English",
"text": "object"
}
]
}
}
],
"validator": "Z101"
}
|
{
"Z1K1": "Z4",
"Z4K1": {
"Z1K1": "Z7",
"Z7K1": "Z885",
"Z885K1": "Z523"
},
"Z4K2": [
{
"Z1K1": "Z3",
"Z3K1": "Z99",
"Z3K2": "Z523K1",
"Z3K3": {
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "object"
}
]
}
}
],
"Z4K3": "Z101"
}
|
Nesting of Errors
editBefore persistence or execution, ZObjects go through a synchronous process of static validation or wellformedness. This validation is done by checking the given ZObject against the JSON Schema definitions found in function-schemata, either in the WikiLambda extension (using opis JSON Schema) or in the NodeJS back-end services (using Ajv JSON schema validator).
Because a ZObject is a nested ZObject, a parent key-value can be not valid due to an error in a child key-value. For example, in the following ZObject:
{
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "true"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": false
}
]
}
The value for the first Z11K2
(line 7) is correct, while the value for the second Z11K2
(line 12) is not. This means that the first ZObject of type Z11/monolingual string
from the list is correct, while the second one is incorrect. The validation error from the second element of this list would then propagate up to its parent objects, and would also raise static validation errors in:
- the whole list (lines 3-14),
- the value of
Z12K1
, and finally, - the whole
Z12/multilingual string
ZObject.
The static validation for the object above will produce a Z5/error object that represents this exact tree of validation errors and follows the branch down, key by key, until reaching to the exact value problem. A simplified representation of the above error is:
{
"Z1K1": "Z5",
"Z5K1": "Z502", // Root error. Z502: "Not wellformed" error
"Z5K2": {
"Z1K1": "Z502",
"Z502K1": "Z526", // What's the internal error? Z526
"Z502K2": { // Okay, let's see it.
"Z1K1": "Z5",
"Z5K1": "Z526", // Z526: "Key value not wellformed" error
"Z5K2": {
"Z1K1": "Z526",
"Z526K1": "Z12K1", // Which is the non-valid key? "Z12K1"
"Z526K2": { // Sure, but why?
"Z1K1": "Z5",
"Z5K1": "Z522", // Z522: "Array element not wellformed" error
"Z5K2": {
"Z1K1": "Z522",
"Z522K1": "1", // Which element is not valid? The one with index "1"
"Z522K2": { // Okay. But, why?!
"Z1K1": "Z5",
"Z5K1": "Z526", // Z526: "Key value not wellformed" error
"Z5K2": {
"Z1K1": "Z526",
"Z526K1": "Z11K2", // Which is the non valid key this time? "Z11K2"
"Z526K2": { // Can I know why?
"Z1K1": "Z5",
"Z5K1": "Z521", // Z521: "ZObjects must not be a number or Boolean or null" error
"Z5K2": {
"Z1K1": "Z521",
"Z521K1": { // What's the offending value?
"Z1K1": "Z99",
"Z99K1": false // Here, this one.
}
}
}
}
}
}
}
}
}
}
}
Please note that the ZObject above has been simplified for readability, but a type such as "Z1K1": "Z526"
is not valid, and should instead be typed with the result of the function call Z885 (error type to type) instead, as explained above.
The following error types are available to describe root or/and branch errors:
- Z502: "Not wellformed" error: This is the root error type for any error tree returned on static validation. It's second key contains the error or errors found during the parsing process.
- Z526: "Key value not wellformed" error: This is a branch error type. It's first parameter gives information of which key contains a non-valid ZObject, and it's second parameter is error propagated from the bottom.
- Z522: "Array element not wellformed" error: This is also a branch error type. It can only be returned during validation of a canonical object, as is the only form that admits JSON arrays. Its first parameter provides index of the error that caused the validation error (starting from 0), while its second parameter contains the propagated error.
Working with Z509/List of Errors
editWhen multiple errors must be returned, these are wrapped in a parent error of type Z509/list of errors
. A error of type Z509 contains a list of Z5/error
ZObjects, and it can contain branch error types as well as leaf error types. For example, an ZObject of type Z3/key
like the following one would raise a validation error for two reasons:
- The key
Z3K2
contains a number, which is a disallowed value type. - The mandatory key
Z3K3
is missing.
{
"Z1K1": "Z3",
"Z3K1": "Z6",
"Z3K2": 34
}
The static validation of this ZObject then would cause a validation error that contains two separate error branches:
{
"Z1K1": "Z5",
"Z5K1": "Z502", // Root error of type Z502: "Not wellformed"
"Z5K2": {
"Z1K1": "Z502",
"Z502K1": "Z509", // What's the internal error? "Z509"
"Z502K2": { // Okay, let's see it.
"Z1K1": "Z5",
"Z5K1": "Z509", // Z509: "List of errors" error
"Z5K2": {
"Z1K1": "Z509",
"Z509K1": [ // What errors were found?
{
"Z1K1": "Z5", // First error from the list:
"Z5K1": "Z526", // Non terminal error of type Z526: "Key value not wellformed"
"Z5K2": {
"Z1K1": "Z526",
"Z526K1": "Z3K2", // Which is the non valid key? "Z3K2"
"Z526K2": { // Why?
"Z1K1": "Z5",
"Z5K1": "Z521", // Z521: "ZObjects must not be a number or Boolean or null" error
"Z5K2": {
"Z1K1": "Z521",
"Z521K1": { // What's the offending value?
"Z1K1": "Z99",
"Z99K1": 34 // Here, this value is wrong.
}
}
}
}
},
{
"Z1K1": "Z5", // Second error from the list:
"Z5K1": "Z511", // Terminal error of type Z511: "Key not found"
"Z5K2": {
"Z1K1": "Z511",
"Z511K1": "Z3K3", // Which is the missing key? "Z3K3"
"Z511K2": { // Where is it missing from?
"Z1K1": "Z99",
"Z99K1": { // From here.
"Z1K1": "Z3",
"Z3K1": "Z6",
"Z3K2": 34
}
}
}
}
]
}
}
}
}
As demonstrated, the list of errors that forms the value of the error Z509/list of errors
can contain branch-level errors (such as the first one of type Z526/key value not wellformed
) as well as leaf errors (like the second one in the list, of type Z511/key not found
. Similarly, an error of type Z509/list of errors
can be found many times and at any level of the error tree.
For example, for a non-valid ZObject such as this one, where the only element in the list of Z11/monolingual strings
is missing two mandatory keys: Z11K1
and Z11K2
:
{
"Z1K1": "Z3",
"Z3K1": "Z6",
"Z3K2": "Z1000K1",
"Z3K3": {
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11"
}
]
}
}
The error of type Z509/list of errors
will not occur until the direct parent of both errors, and both errors in the list will be leaf errors of the type Z511/key not found
:
{
"Z1K1": "Z5",
"Z5K1": "Z502", // Root error of type Z502: "Not wellformed"
"Z5K2": {
"Z1K1": "Z502",
"Z502K1": "Z526", // What's the internal error? Z526
"Z502K2": { // Okay, let's see it.
"Z1K1": "Z5",
"Z5K1": "Z526", // Non terminal error of type Z526: "Key value not wellformed"
"Z5K2": {
"Z1K1": "Z526",
"Z526K1": "Z3K3", // Which is the non-valid key? "Z3K3"
"Z526K2": { // Okay, why?
"Z1K1": "Z5",
"Z5K1": "Z526", // Non terminal error of type Z526: "Key value not wellformed"
"Z5K2": {
"Z1K1": "Z526",
"Z526K1": "Z12K1", // Which is the non-valid key? "Z12K1"
"Z526K2": { // Again, why?
"Z1K1": "Z5",
"Z5K1": "Z522", // Non terminal error of type Z522: "Array element not wellformed"
"Z5K2": {
"Z1K1": "Z522",
"Z522K1": "0", // Which element is not valid? The one with index "0"
"Z522K2": { // What's wrong with it?
"Z1K1": "Z5",
"Z5K1": "Z509", // Z509: "List of errors" error
"Z5K2": {
"Z1K1": "Z509",
"Z509K1": [
{
"Z1K1": "Z5", // First error from the list, a terminal one:
"Z5K1": "Z511", // Z511: "Key not found" error
"Z5K2": {
"Z1K1": "Z511",
"Z511K1": "Z11K1", // Which is the missing key? "Z11K1"
"Z511K2": { // Where is it missing from?
"Z1K1": "Z99",
"Z99K1": { // From here.
"Z1K1": "Z11"
}
}
}
},
{
"Z1K1": "Z5", // Second error from the list, also a terminal one:
"Z5K1": "Z511", // Z511: "Key not found" error
"Z5K2": {
"Z1K1": "Z511",
"Z511K1": "Z11K2", // Which is the missing key? "Z11K2"
"Z511K2": { // Where is it missing from?
"Z1K1": "Z99",
"Z99K1": { // From here.
"Z1K1": "Z11"
}
}
}
}
]
}
}
}
}
}
}
}
}
}
}
Pre-defined Error Types
editPre-defined Error Types have reserved the ZID range from Z500-Z599. Here's a list of all the pre-defined Error Types that are currently being used by the different parts of the Wikifunctions stack. The table also gives details of their arguments, their names and their relevant scope. After each argument, in parentheses, we indicate the type of values taken by the argument.
ZID | Scope | Arguments | Name |
---|---|---|---|
Z500 | global | error information (Z1) | Generic error |
Z501 | global | error message (Z6), input (Z6) | JSON syntax error |
Z502 | global | subtype (Z50), value (Z5) | Not wellformed |
Z503 | executor | feature name (Z6) | Not implemented yet |
Z504 | global | ZID (Z6) | ZID not found |
Z505 | executor | expected (Z6), actual (Z6), args (Z10) | Number of arguments mismatch |
Z506 | executor | expected (Z4), actual (Z4), arg (Z1), propagated error (Z5) | Argument type mismatch |
Z507 | executor | function call (Z7), propagated error (Z5) | Error in evaluation |
Z508 | executor | key (Z39), object (Z99) | Competing keys |
Z509 | global | errors (Z10) | List of errors |
Z510 | - | - | NIL |
Z511 | global | key (Z39), object (Z99) | Key not found |
Z512 | global | expected result (Z99), actual result (Z99) | Test failed |
Z513 | executor | object (Z99) | Resolved persistent object without value |
Z514 | executor | implementation (Z14) | Built-in does not exist |
Z515 | executor | implementation (Z14) | Built-in ID is erroneous |
Z516 | executor | argument (Z18), bad value (Z99) | Argument value error |
Z517 | executor | expected type (Z4), actual type (Z4), returned value (Z1), propagated error (Z5) | Return type mismatch |
Z518 | executor | expected type (Z4), object (Z1), propagated error (Z5) | Object type mismatch |
Z519 | global | object (Z1) | Undefined list type |
Z520 | global | object (Z1) | Wrong list type |
Z521 | global | offending value (Z99) | ZObjects must not be a number or Boolean or null |
Z522 | global | index of offending element (Z6), propagated error (Z5) | Array element not wellformed |
Z523 | global | object (Z99) | Missing Z1K1 |
Z524 | global | value (Z99) | Z1K1 must not be a string or array |
Z525 | global | key (Z6) | Invalid key |
Z526 | global | key (Z39), propagated error (Z5) | Key value not wellformed |
Z527 | 🆓 | ||
Z528 | 🆓 | ||
Z529 | 🆓 | ||
Z530 | 🆓 | ||
Z531 | global | object (Z99) | Z6 must have 2 keys |
Z532 | global | object (Z99) | Z6 without a Z6K1 |
Z533 | global | value of Z6K1 (Z99) | Z6K1 must be a string |
Z534 | global | object (Z99) | Z9 must have 2 keys |
Z535 | global | object (Z99) | Z9 without a Z9K1 |
Z536 | global | value of Z9K1 (Z99) | Z9K1 must be a string |
Z537 | global | value of Z9K1 (Z99) | Z9K1 must look like a reference |
Z538 | extension | page title (Z6) | Wrong namespace |
Z539 | extension | page title (Z6) | Wrong content type |
Z540 | extension | language code (Z6) | Invalid language code |
Z541 | extension | language code (Z6) | Language code not found |
Z542 | extension | expected type (Z4), actual type (Z4) | Unexpected ZObject type |
Z543 | extension | type name (Z6) | Type not found |
Z544 | extension | type ZID (Z6), type name (Z6), existing type name (Z6) | Conflicting type names |
Z545 | extension | type ZID (Z6), type name (Z6), existing type ZID (Z6) | Conflicting type ZIDs |
Z546 | extension | type ZID (Z6), type name (Z6) | Built-in type not found |
Z547 | global | input (Z99) | Invalid format |
Z548 | global | error message (Z6), input (Z99) | Invalid JSON |
Z549 | global | reference value (Z6) | Invalid reference |
Z550 | global | reference value (Z6) | Unknown reference |
Z551 | global | key (Z39), expected type (Z6), actual type (Z6) | Schema type mismatch |
Z552 | global | index of offending element (Z6), expected type (Z4), actual content (Z99) | Array element type mismatch |
Z553 | global | root zobject (Z99) | Disallowed root type |
Z554 | extension | clashing ZID (Z6), language (Z6) | Label for a given language clashes with another ZObject's label |
Z555 | extension | ZID (Z6), page title (Z6) | Unmatching ZID and page title |
Z556 | extension | title (Z6) | Invalid page title |
Z557 | extension | message (Z6) | User does not have permission to edit |
Z558 | global | programming language (Z6) | Invalid programming language |
Z559 | extension | - | User not permitted to evaluate function |
Z560 | orchestrator | evaluation result (Z99) | Invalid evaluation result |
Z561 | evaluator | propagated error (Z5) | Invalid evaluation request |
Z562 | evaluator | missing property (Z6) | Incomplete evaluation request |
Z563 | evaluator | call (Z6) | Call by non-reentrant executor |
Z564 | executor | contents (Z6) | Invalid executor response |
Z565 | executor | missing property (Z6) | Incomplete executor request |
Z566 | 🆓 | ||
Z567 | 🆓 | ||
Z568 | 🆓 | ||
Z569 | 🆓 | ||
Z570 | orchestrator | orchestrator rate limit (Z6) | Reached rate limit in orchestrator |
Z571 | evaluator | evaluator rate limit (Z6) | Reached rate limit in evaluator |
Z572 | orchestrator | recursion limit (Z6), function name (Z6) | Reached recursion limit in orchestrator |
Z573 | evaluator | recursion limit (Z6), function name (Z6) | Reached recursion limit in evaluator |
Z574 | orchestrator | time limit (Z6) | Reached time limit in orchestrator |
Z575 | evaluator | time limit (Z6) | Reached time limit in evaluator |
Making your own Error Type
editUsers can also define custom error types, by creating a ZObject of type Z50/Error type
on the Wikifunctions UI or via the Wikifunctions API.
A Z50/Error type
object has a similar shape to a Z4/Type
, and defines as its only value (Z50K1) an array of keys that an instance of this error type created on runtime will need to have. Each key is a relevant piece of information with a label that describes it.
Say, for example, that we wish to create the function that formats a date passed as an input. If the date is incorrect (e.g. "32/05/1985"), we want to return a "Incorrect date" error, for which we shall create that particular error type. The type will contain one only key, with the string representation of the erroneous date passed as an input.
Using the API wikilambda_edit
and passing the following object as the data to be saved:
{
"Z1K1": "Z2", // We create a persisted object
"Z2K1": { // With Zid Z0, as it will be automatically assigned
"Z1K1": "Z6",
"Z6K1": "Z0"
},
"Z2K2": {
"Z1K1": "Z50",
"Z50K1": [ // One key in the array under Z50K1
{
"Z1K1": "Z3",
"Z3K1": "Z50",
"Z3K2": "Z0K1", // The Zid part of the key also is Z0
"Z3K3": { // With the label describing the key
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "erroneous date"
}
]
}
}
]
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Incorrect date" // And a label describing the error
}
]
}
}
If the call is successful (the format of the error is correct, the label is unique, etc.), the API will return the Zid assigned to this error type.
{
"wikilambda_edit": {
"success": "",
"articleId": 1095,
"title": "Z10000",
"page": "Z10000"
}
}
How to raise an Error in your Implementation
editThere are two types of function implementations in WikiFunctions available to users: those specified as programming language code in a supported language, and those specified as a composition of other WikiFunctions functions. Here we show how to raise an error in each of these types of implementations.
Code
editIn a programming language code implementation, an error can be raised in the normal way provided by that programming language. (For example, in Python your implementation would use the keyword raise
followed by the specification of an exception. In JavaScript you would use the keyword throw
followed by the specification of an exception.) The WikiFunctions system code, which is responsible for executing your implementation, will catch the exception and package it into an appropriate Z5 / Error
object to be returned from the execution. (More particularly, in Python the resulting Z5 / Error
object will contain the value of e.args[0]
extracted from the caught exception e
. In JavaScript, it will contain the value of e.message
extracted from e
.) Every WikiFunctions function call returns a Z22 / Evaluation result
object, and the resulting Z5 / Error
object will be returned as the value of the Z22K2 / metadata
key.
Composition
editIn a composition implementation, an error can be raised by specifying a Z7 / Function call
to the function Z820 / Trigger metadata
, and passing a Z5 / Error
object as the value of that function's Z820K1 / error
argument. Here is what such a call looks like, assuming an error of type Z502 / not well-formed error
:
{
"type": "Function call",
"function": "Trigger metadata",
"error": {
"type": "Error",
"error type": "not well-formed error",
"error value": {
...
}
}
}
|
{
"Z1K1": "Z7",
"Z7K1": "Z820",
"Z820K1": {
"Z1K1": "Z5",
"Z5K1": "Z502",
"Z5K2": {
...
}
}
}
|
Your call to Z820 / Trigger metadata
will cause the given Z5 / Error object
to be returned from the execution of the composition (as the value of the Z22K2 / metadata
key of a Z22 / Evaluation result
object).