joi
>=17.0.0 <19.0.0postconditions11functions7last verified2026-06-24coverage score88%Postconditions: what we check
- validate · validate-returns-errorerrorWhendata fails validation against the schemaReturns{error: ValidationError, value: any} where error contains validation failure detailsRequired handlingCaller MUST check result.error property before using result.value. Without checking error, invalid data will pass through silently, leading to data corruption, business logic errors, or security vulnerabilities. Use pattern: const { error, value } = schema.validate(data); if (error) { /* handle */ }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- validateAsync · validateasync-rejectserrorWhendata fails validation against the schemaThrows
Promise rejection with ValidationErrorRequired handlingCaller MUST wrap validateAsync() in try-catch or use .catch() handler. Without error handling, validation failures cause unhandled promise rejections that crash the application. Use pattern: try { const value = await schema.validateAsync(data); } catch (error) { /* handle */ }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - assert · assert-throwserrorWhendata fails validation against the schemaThrows
ValidationErrorRequired handlingCaller MUST wrap Joi.assert() in try-catch block. Without error handling, validation failures throw uncaught exceptions that crash the application. Use pattern: try { Joi.assert(value, schema); } catch (error) { /* handle */ }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - attempt · attempt-throwserrorWhendata fails validation against the schemaThrows
ValidationErrorRequired handlingCaller MUST wrap Joi.attempt() in try-catch block. Without error handling, validation failures throw uncaught exceptions that crash the application. Use pattern: try { const validated = Joi.attempt(value, schema); } catch (error) { /* handle */ }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[4] - compile · compile-invalid-schema-throwswarningWhenschema argument is undefined, an empty array, or contains non-plain objectsThrows
AssertError (extends Error) from @hapi/hoek — message: 'Invalid undefined schema' or 'Invalid empty array schema' or 'Schema can only contain plain objects'Required handlingCaller MUST wrap Joi.compile() in try-catch when schema is built dynamically (e.g. from user input, config files, or database). Static schema definitions compiled at module load are safe if the schema literal is correct. Without error handling, invalid schema definitions throw at runtime and crash the request handler or service startup. Use pattern: try { const schema = Joi.compile(rawSchema); } catch (err) { /* handle */ }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - compile · compile-version-mismatch-throwswarningWhenschema was compiled with a different version of joi (legacy: false, default)Throws
AssertError — message: 'Cannot mix different versions of joi schemas: <version> <version>'Required handlingWhen using Joi.compile() with dynamically loaded schemas from external sources (plugins, serialized schemas), version mismatches throw AssertError. Use Joi.compile(schema, { legacy: true }) to allow older schema versions, or ensure all schemas are compiled with the same joi version.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - extend · extend-empty-extensions-throwswarningWhenextend() is called with zero argumentsThrows
AssertError from @hapi/hoek — message: 'You need to provide at least one extension'Required handlingCaller MUST pass at least one extension definition. When extensions are loaded dynamically (from a plugin loader, config file, or filesystem scan), wrap Joi.extend() in try-catch in case the plugin list is empty after filtering. Without error handling, an empty extension list crashes module initialization. Use pattern: try { customJoi = Joi.extend(...extensions); } catch (err) { /* handle */ }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - extend · extend-invalid-extension-shape-throwserrorWhenextension object fails Schemas.extension validation (missing type, invalid base, malformed rules)Throws
ValidationError from this.assert(extension, Schemas.extension) — wrapped AssertErrorRequired handlingCaller MUST validate extension definitions before passing to extend() when extensions come from external sources (user-defined plugins, dynamic config). Each extension must have a string type field and optional base schema, prepare, coerce, validate, rules, and messages fields matching the documented shape. Use pattern: try { customJoi = Joi.extend(extensionDef); } catch (err) { /* handle */ }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - extend · extend-override-existing-type-throwswarningWhenextension attempts to define a type name that is not already in the instance's _types set (e.g. trying to override a primitive type name like 'string' without inheriting it as base)Throws
AssertError — message: 'Cannot override name <type>'Required handlingWhen extending Joi to add new type names, ensure the name does not collide with built-in primitive types (string, number, boolean, etc.) unless explicitly inheriting via base. Common pitfall when adding a custom 'email' or 'url' type. Use pattern: try { customJoi = Joi.extend({ type: 'myCustomType', base: Joi.string(), ... }); } catch (err) { /* handle */ }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[7] - defaults · defaults-non-function-modifier-throwswarningWhenmodifier argument is not a function (e.g. undefined, object, null)Throws
AssertError from @hapi/hoek — message: 'modifier must be a function'Required handlingCaller MUST pass a function as the modifier argument. When the modifier comes from external config or is computed dynamically, validate it is a function before calling Joi.defaults(). Use pattern: try { customJoi = Joi.defaults(modifier); } catch (err) { /* handle */ }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - defaults · defaults-modifier-returns-non-schema-throwserrorWhenmodifier function returns a value that is not a joi schema objectThrows
AssertError from @hapi/hoek — message: 'modifier must return a valid schema object'Required handlingModifier function MUST return a joi schema for every input schema. Common mistakes: forgetting to return (returns undefined), returning a plain object instead of calling .object() on it, returning the input unchanged when the modifier mutates instead of returning. Use pattern: Joi.defaults((schema) => schema.required()) — always return the schema.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
- [5]raw.githubusercontent.com/hapijs/joi/masterhapijs/joi · API.md
- [6]github.com/hapijs/joi/blobhapijs/joi · compile.js
- [7]github.com/hapijs/joi/blobhapijs/joi · index.js
- [8]github.com/hapijs/joi/blobhapijs/joi · schemas.js
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources: joi
Package: joi Version: 17.x - 18.x Category: validation Last Updated: 2026-02-26 Status: ✅ COMPLETE
Official Documentation
Primary Source
- API Reference: https://joi.dev/api
- Comprehensive documentation of all validation methods
- Error handling patterns and best practices
- ValidationError structure and properties
Repository
- GitHub: https://github.com/hapijs/joi
- Official repository under hapijs organization
- 21.2k+ stars, actively maintained
- No active security advisories
Key Behavioral Requirements
1. schema.validate() - Synchronous Validation
Documentation: https://joi.dev/api/?v=17.13.3#anyvalidatevalue-options
Behavior:
- Returns
{ error, value, warning, artifacts } - Does NOT throw errors (returns error object instead)
- Must check
.errorproperty before using.value
Quote from Docs:
"Returns an object with the following keys:
value- the validated and normalized value,error- the validation errors if found."
Risk: Invalid data passes through silently if error is not checked
2. schema.validateAsync() - Asynchronous Validation
Documentation: https://joi.dev/api/?v=17.13.3#anyvalidateasyncvalue-options
Behavior:
- Returns a Promise
- Rejects promise on validation failure
- Must use try-catch or .catch() handler
Quote from Docs:
"Returns a Promise that resolves to the validated value or rejects with validation errors."
Risk: Unhandled promise rejection crashes application
3. Joi.assert() - Assertion-Based Validation
Documentation: https://joi.dev/api/?v=17.13.3#assertvalue-schema-message-options
Behavior:
- Throws ValidationError on failure
- No return value (void)
- Must wrap in try-catch
Quote from Docs:
"Throws on validation failure."
Risk: Application crash on invalid input if not caught
4. Joi.attempt() - Throwing Validation
Documentation: https://joi.dev/api/?v=17.13.3#attemptvalue-schema-message-options
Behavior:
- Returns validated value on success
- Throws ValidationError on failure
- Must wrap in try-catch
Quote from Docs:
"Returns the validated value or throws."
Risk: Application crash on invalid input if not caught
Real-World Usage Examples
Example 1: Docusaurus
Repository: https://github.com/facebook/docusaurus
Usage: Configuration and front-matter validation
Pattern: Proper error checking with const { error, value } = schema.validate()
File: packages/docusaurus-utils-validation/src/validationUtils.ts
const {error, warning, value} = finalSchema.validate(options, {
convert: false,
});
printWarning(warning);
if (error) {
throw error; // ✅ Properly checks and throws
}
return value;
Example 2: Next.js with-joi Example
Repository: https://github.com/vercel/next.js/tree/canary/examples/with-joi Usage: API request body validation Pattern: Middleware wrapper for validation
File: examples/with-joi/pages/api/people.js
const personSchema = Joi.object({
age: Joi.number().required(),
name: Joi.string().required(),
});
router.post(validate({ body: personSchema }), (req, res) => {
const person = req.body; // ✅ Middleware handles validation
return res.status(201).json({ data: person });
});
Security & CVE Analysis
Search Date: 2026-02-26
Sources Checked:
- GitHub Security Advisories: 0 active advisories
- Snyk Vulnerability Database: No behavioral CVEs found
- NVD Database: No relevant entries
Finding: No CVEs found related to validation behavior. The primary risk is developer misuse (not checking errors), not library vulnerabilities.
Common Mistakes
Mistake 1: Not Checking .error Property
// ❌ WRONG
const { value } = schema.validate(data);
doSomething(value); // May be invalid!
Impact: Invalid data passes through silently
Fix: Always check error: if (error) { /* handle */ }
Mistake 2: Missing try-catch for validateAsync()
// ❌ WRONG
const value = await schema.validateAsync(data); // No try-catch
Impact: Unhandled promise rejection crashes app Fix: Wrap in try-catch block
Mistake 3: Using assert/attempt without try-catch
// ❌ WRONG
Joi.assert(data, schema); // Will crash if validation fails
Impact: Application crash on invalid input Fix: Wrap in try-catch block
Production Usage Statistics
Analysis Date: 2026-02-26 Repos Analyzed: Docusaurus, Next.js Validation Calls Found: 3 Proper Error Handling: 3 (100%)
Conclusion: High-quality production codebases consistently use proper error handling patterns.
Contract Decisions
Severity: ERROR
All validation methods require error handling because:
- validate() - Silent failures lead to invalid data in system
- validateAsync() - Unhandled rejections crash applications
- assert()/attempt() - Uncaught exceptions crash applications
Scope
Contract covers:
schema.validate()- Requires checking .error propertyschema.validateAsync()- Requires try-catchJoi.assert()- Requires try-catchJoi.attempt()- Requires try-catch
Contract does NOT cover:
- Schema definition methods (
.string(),.object(), etc.) - Constraint methods (
.required(),.min(),.max(), etc.) - Warning handling (warnings are non-fatal)
References
-
Official API Documentation https://joi.dev/api Comprehensive reference for all methods
-
Validation Error Structure https://joi.dev/api/#validationerror Details on error object properties
-
Best Practices Guide https://joi.dev/api/#general-usage Recommended patterns for validation
-
Production Examples
- Docusaurus validation utilities
- Next.js API validation examples
Research Completed: 2026-02-26
Research Files: dev-notes/package-onboarding/joi/.onboarding/research/