Ajv: Custom Error Messages

Created on 20 Jan 2016  路  32Comments  路  Source: ajv-validator/ajv

The docs are a bit terse so I'm not entirely sure on this, but do you have the ability to define custom error messages?

feature

Most helpful comment

:+1:
This feature would be very interesting to me, too bad it isn't handled in ajv... As amcdnl said, a format like (jsen) would be cool :

var schema = {
    type: 'number',
    minimum: 18,
    messages: {
        type: 'Must be a number',
        minimum: 'No children...'
    }
}

also with the hability to provide a function :

messages: {
     length: function(val, obj) {
           return obj.otherProperty === true ? "You're wrong" : "You're not right";
     }
}

All 32 comments

Not in ajv at the moment. You can create your own messages based on error.params. How would you envisage defining them I wonder?

My idea was this: https://github.com/json-schema/json-schema/wiki/Custom-error-messages-(v5-proposal), but I am not sure yet what the templating approach should be. I'd like to have something like relative-json-pointer-expression, to be honest, that would include interpolated strings.

I'm not entirely in love with this API, but this is kinda what I was thinking: https://github.com/bugventure/jsen#custom-errors

yeah, I saw it. It won't be exactly that, for sure.

:+1:
This feature would be very interesting to me, too bad it isn't handled in ajv... As amcdnl said, a format like (jsen) would be cool :

var schema = {
    type: 'number',
    minimum: 18,
    messages: {
        type: 'Must be a number',
        minimum: 'No children...'
    }
}

also with the hability to provide a function :

messages: {
     length: function(val, obj) {
           return obj.otherProperty === true ? "You're wrong" : "You're not right";
     }
}

+1

I am thinking about implementing invalidMessage and requiredMessage as custom keywords and add them to ajv-keywords. This way those who need this functionality can use those keywords without polluting the namespace and increasing the size of ajv for everybody else.

These keywords will analyse errors collected so far for the same property/item and if it was invalid/required the error will be added. Then it's possible to filter those errors out (using keyword property of the error object).

What do you think?

hi @epoberezkin . Just to add my voice.
I believe this is a much needed feature.
If I understood right your approach. If you get a validation error and if there is an invalidMessage / requiredMessage defined for that property it will be added into the error object
Is that so?

Isn't this already achievable using verbose:true and accessing parentSchema.invalidMessage?
What will the keyword add that cannot be done already?

The keyword schema is indeed accessible, but depending on the format of this schema the keyword can do more - e.g., it can have different errors for different rules or maybe it can merge and remove collected errors into one error.

But you are right, on a simple level all that's needed is to add some info in the schema using any keyword and to access it via error object to create the list of application specific errors.

+1.

I'm writing an API that will use ajv (hopefully) and I need to have exact control over the messages for translation purposes.

Ideally I'd provide a default error message for each property, sometimes not caring about what exactly caused the error (e.g. type or required).

I'm currently using revalidator, which allows custom messages, but I'm looking forward to switching to ajv. Indeed, a much needed feature!

@crkepler The suggestion above by @claudio-viola can be helpful. In any case this functionality will be implemented as an [optional] custom keyword. What is the syntax for custom errors used in revalidator? I don't see it in the repo.

Also revalidator doesn't seem to be particularly close to the standard (looking at the docs and at the benchmark).

@epoberezkin: first, thanks so much for your amazing work done here. I agree, revalidator isn't standard and that's why I'm recommending our team to leave it and use ajv. Here's an example of how we implement custom messages in revalidator (FYI only, I don't necessarily suggest this approach, although it works for us):

{
    "type": "object",
    "properties": {
        "organizationId": {
            "type": ["string", "null"],
            "required": false,
            "allowEmpty": false,
            "minLength": 1,
            "maxLength": 80,
            "message": "When supplied, 'organizationId' must be a string between 1 and 80 characters long."
        },
        "name": {
            "type": "string",
            "required": true,
            "allowEmpty": false,
            "minLength": 1,
            "maxLength": 250,
            "message": "The organization 'name' can't be empty and it must be a string between 1 and 250 characters long."
        },

Thanks for much for considering this feature, it'd be a tremendous help for us. Keep up the great work!

Thank you. I can also see in revalidator test that it supports messages keyword to define keyword-specific messages.

So I am considering implementing custom keyword errorMessages that can be a string or an object.

If it's a string "error_message" and the validation fails on the current level it will add an error object:

{
  keyword: 'errorMessages',
  message: 'error_message',
  params: { keywords: ['...' /*, '...', ... */] } // failing keywords
  // other properties as usual
}

If it's an object, then it's keys are keywords and the values are strings with errors for each keyword (a bonus would be to allow templates referring to the keyword schema and data values). required and dependencies keywords are special, they should also allow property specific messages (as there is no such thing as "required": true in draft 4 and instead the array of properties should be used on the object level).

{
  "properties": {
    "size": {
      "type": "number",
      "minimum": 4,
      "multipleOf": 2,
      "errorMessages": {
        "type": "size should be a number",
        "minimum": "size should be bigger or equal to 4",
        "multipleOf": "size should be even"
      }
    }
  },
  "required": ["size"],
  "errorMessages": {
    "required": {
      "size": "size is mandatory"
    }
  }
}

In this case instead of adding additional error objects, the keyword would add the property errorMessage to them (it's better than replacing message property as it would still allow functions such as errorsText produce meaningful results and also i18n would not replace errors in those additional properties.

Or maybe it's better to always add additional objects - they can be easily filtered out. Need to think about it.

This custom keyword will be defined in ajv-keywords package so it would be quite easy to use.

Suggestions? @amcdnl @Xamage @joaoreynolds @claudio-viola @crkepler

Or maybe even in a separate package ajv-errors, ajv-keywords would stay for custom validation keywords then...

@epoberezkin all the approaches you're suggesting are flexible and easy to implement for us users. So, I have no preference for the implementation you choose. However, using a separate package _seems_ to be less risky for development (and easier to test?), and also more extensible and decoupled from the existing code.

Newbie to Ajv and JavaScript here... For me, it is not clear what this issue is about. Is it about adding custom keywords and having custom error messages associated to them? Or is it about adding custom error messages to existing, Ajv provided keywords?

About the latter, custom error messages for different validation failures, not necessarily based on a single keyword. Custom keywords are already supported.

PLEDGE: Contribute any amount to this project and I will match your contribution 100%, to reach the project goal of US$ 500.00.

There's still US$ 425.00 to go, so my needed match limit is US$ 212.50, which I'll happily contribute as others chip in. I prefer not to be the sole contributor and would like more people involved.

My pledge is valid until 4-July-2017, one year after the first contribution was made.

Please, contribute any amount you can:

https://www.bountysource.com/issues/30029477-custom-error-messages

@epoberezkin is there a final standard that should be implemented?

hey guys, what is the right way to do this then? cannot find concrete examples in the doc. I just want to be able to change the default error messages for custom ones. thanks!

@danielanteloagra to change the error messages you need to process them in your code after validation, there is no other way at the moment. You can look in the code that ajv-i18n package uses for this purpose and follow the same pattern (ajv-i18n doesn't allow defining your own messages at the run time, but it can help understand how to do change the validation messages).

thanks @epoberezkin
That's a shame, as we are trying to share the schema between systems... not nice that each system then has to do work to override the validation messages :( is there a plan to integrate it into the actual json in the future?

This issue is a plan :)

@crkepler https://github.com/epoberezkin/ajv-errors/blob/master/SPEC.md - let me know what you think

@epoberezkin: It looks awesome, exactly as I'd expect!

I only have one suggestion, if that's OK. What do you think about adding a "catch all" message, for cases in which we don't want to specify an individual message for every single constraint? Using your example, see the "message" property I added below as an example:

"properties": { "size": { "type": "number", "minimum": 4, "multipleOf": 2, "errorMessage": { "type": "size should be a number", "minimum": "size should be bigger or equal to 4", "multipleOf": "size should be even" "message": "size should be an even number, greater or equal than 4" } }
"message" in this case, is a "catch all" error message, with the lowest priority of them all. If there's a an errorMessage property for the violation, use it. If not, use "message". If there's no "message" defined, nothing is returned. Does this make sense? What do you think?

Thanks again for your thorough analysis on this issue. It's really exciting stuff!

@crkepler There are two scenarios possible here: 1) no messages that are defined match but there are some other errors. 2) some messages match and there are some other errors as well.

The example above makes sense in scenario 1, but not much sense in scenario 2. Potentially we could have two catch-all messages:

  1. defaultMessage (like in your example, for scenario 1
  2. otherErrorsMessage (for scenario 2, it could be "size is invalid")

Not sure I like it to be honest - it's getting complicated :)

A simpler options would be to have only one defaultMessage and use it:

  1. only in scenario 1
  2. only in scenario 2
  3. in both cases

What do you think?

Also as we are defining catch all messages (and even without this extension we need them in case string rather than object with keyword-specific messages is used) than we probably need to be able to some elements from errors (e.g. the list of keywords) in the string templates...

@crkepler also I think error classification could be a part of this feature, it was asked a couple of times.

@epoberezkin - What a privilege it was talking to you on Skype; a great pleasure to meet you. You're a first class person, not to mention one of the smartest developers I've ever met.

The bounty goal has been achieved and it was an honor for me to contribute to this project. I hope you get to implement this complex feature -- it looks so simple at first, but it is not.

@crkepler thank you for the support. I will do it.

Some limited functionality is available in ajv-errors, more to follow.

1.0.0 published

Was this page helpful?
0 / 5 - 0 ratings

Related issues

darrin picture darrin  路  5Comments

krishnanms picture krishnanms  路  4Comments

vivekgalatage picture vivekgalatage  路  5Comments

JonathanWilbur picture JonathanWilbur  路  3Comments

jabberwo picture jabberwo  路  7Comments