Jump to content

Abstract Wikipedia/Representation of errors

From Meta, a Wikimedia project coordination wiki

Abstract Wikipedia via mailing list Abstract Wikipedia on IRC Wikifunctions on Telegram Wikifunctions on Mastodon Wikifunctions on Twitter Wikifunctions on Facebook Wikifunctions on YouTube Wikifunctions website Translate

Motivation and rationale

In 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

Every 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

Error 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:

  1. Adding a reference to the error type Z523 as the value of Z5K1, and
  2. Creating an instance of the error type Z523.

Instances of Error Types

To 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"
}

Try it in notwikilambda

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

Before 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:

  1. the whole list (lines 3-14),
  2. the value of Z12K1, and finally,
  3. 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

When 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:

  1. The key Z3K2 contains a number, which is a disallowed value type.
  2. 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

Pre-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

Users 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

There 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

In 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

In 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).