JSON Schema Validator (javascript, written by Codex)
envgap__codex__javascript-t1-2
Written by a coding agent; not on GitHubWritten 2026-03-02
01 / FAILURE SIGNATURE
As the study recorded it
None
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/javascript-t1 #2 · read the task the agent was given
Codex wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: JSON Schema Validator Write a program that validates JSON documents against user-defined schema rules. It should support type checking, required fields, nested object validation, array constraints, and pattern matching. It should collect all violations — not stop at the first — and produce a detailed validation report. FUNCTIONAL REQUIREMENTS: - Accept two command-line arguments: a JSON data file and a JSON schema file - Support validation rules: type checking (string, number, boolean, array, object, null), required fields, min/max for numbers, minLength/maxLength for strings, enum values, regex pattern matching - Validate nested objects and arrays recursively to any depth - Validate array items (all items must conform to a specified type or sub-schema) - Collect ALL validation errors across the entire document, reporting the full JSON path for each violation (e.g., $.users[2].email) - Print the validation report to the console with one line per violation showing path, expected rule, and actual value - Save the validation report as validation_report.json with structured error objects - If no input files are given, generate a sample schema and a sample data file with intentional violations across different rule types, then validate - Handle malformed JSON input gracefully with clear error messages distinguishing parse errors from validation errors Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
4 files, exactly as written, before any repair.
package-lock.json
{
"name": "json-schema-validator",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "json-schema-validator",
"version": "1.0.0"
}
}
}
package.json
{
"name": "json-schema-validator",
"version": "1.0.0",
"description": "CLI JSON schema validator with recursive rule checking and full violation reporting.",
"type": "module",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"validate": "node src/index.js"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {},
"devDependencies": {}
}
README.md
# JSON Schema Validator (JavaScript) CLI tool for validating JSON data against user-defined schema rules with full error collection. ## Features - Accepts `data.json` and `schema.json` as CLI arguments. - Supports rules: - `type`: `string`, `number`, `boolean`, `array`, `object`, `null` - `required` for objects - `min`/`max` (and `minimum`/`maximum`) for numbers - `minLength`/`maxLength` for strings - `enum` - `pattern` (regex) - `minItems`/`maxItems` for arrays - Recursive validation for nested objects and arrays. - Validates every array item with `items` schema. - Collects all violations with full JSON paths (e.g., `$.users[2].email`). - Prints one line per violation in console. - Saves structured `validation_report.json`. - If no input files are provided, generates sample schema/data with intentional violations and validates them. - Distinguishes JSON parse errors from validation errors. ## Requirements - Ubuntu 22.04 - Node.js 20+ (LTS) ## Dependencies (Pinned) No external dependencies are used. - Direct dependencies: none - Transitive dependencies: none `package-lock.json` is included and pinned (`lockfileVersion: 3`). ## Setup ```bash cd TMLR/code_generation/codex_generated/p_02/javascript npm install ``` ## Run Validate custom files: ```bash npm run validate -- ./data.json ./schema.json ``` Generate sample files and validate: ```bash npm start ``` ## Output - Console: one line per violation with path, expected rule, and actual value. - File: `validation_report.json` with structured error objects. - If no inputs: `sample_data.json` and `sample_schema.json` are generated.
src/index.js
#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
function jsonTypeOf(value) {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
return typeof value;
}
function escapeRegExpError(pattern, message) {
return `invalid regex pattern "${pattern}": ${message}`;
}
function valuePreview(value) {
if (value === undefined) return "undefined";
if (typeof value === "string") return JSON.stringify(value);
if (value === null || typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (Array.isArray(value)) return `<array length=${value.length}>`;
return "<object>";
}
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a === null || b === null) return a === b;
if (Array.isArray(a) !== Array.isArray(b)) return false;
if (Array.isArray(a)) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i += 1) {
if (!deepEqual(a[i], b[i])) return false;
}
return true;
}
if (typeof a === "object") {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) return false;
for (const key of aKeys) {
if (!Object.hasOwn(b, key)) return false;
if (!deepEqual(a[key], b[key])) return false;
}
return true;
}
return false;
}
function matchesType(value, expectedType) {
const actualType = jsonTypeOf(value);
if (expectedType === "number") return actualType === "number";
if (expectedType === "string") return actualType === "string";
if (expectedType === "boolean") return actualType === "boolean";
if (expectedType === "array") return actualType === "array";
if (expectedType === "object") return actualType === "object";
if (expectedType === "null") return actualType === "null";
return true;
}
function addError(errors, pathText, rule, expected, actualValue) {
errors.push({
path: pathText,
rule,
expected,
actual: valuePreview(actualValue),
});
}
function validateAgainstSchema(data, schema, pathText, errors) {
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
return;
}
if (Object.hasOwn(schema, "type")) {
const declaredType = schema.type;
const allowedTypes = Array.isArray(declaredType) ? declaredType : [declaredType];
const matches = allowedTypes.some((t) => matchesType(data, t));
if (!matches) {
addError(
errors,
pathText,
"type",
Array.isArray(declaredType) ? declaredType.join(" | ") : String(declaredType),
data,
);
return;
}
}
if (Array.isArray(schema.enum)) {
const found = schema.enum.some((allowed) => deepEqual(allowed, data));
if (!found) {
addError(errors, pathText, "enum", JSON.stringify(schema.enum), data);
}
}
const dataType = jsonTypeOf(data);
if (dataType === "number") {
if (typeof schema.min === "number" && data < schema.min) {
addError(errors, pathText, "min", `>= ${schema.min}`, data);
}
if (typeof schema.max === "number" && data > schema.max) {
addError(errors, pathText, "max", `<= ${schema.max}`, data);
}
if (typeof schema.minimum === "number" && data < schema.minimum) {
addError(errors, pathText, "minimum", `>= ${schema.minimum}`, data);
}
if (typeof schema.maximum === "number" && data > schema.maximum) {
addError(errors, pathText, "maximum", `<= ${schema.maximum}`, data);
}
}
if (dataType === "string") {
if (typeof schema.minLength === "number" && data.length < schema.minLength) {
addError(errors, pathText, "minLength", `length >= ${schema.minLength}`, data);
}
if (typeof schema.maxLength === "number" && data.length > schema.maxLength) {
addError(errors, pathText, "maxLength", `length <= ${schema.maxLength}`, data);
}
if (typeof schema.pattern === "string") {
try {
const regex = new RegExp(schema.pattern);
if (!regex.test(data)) {
addError(errors, pathText, "pattern", `match /${schema.pattern}/`, data);
}
} catch (error) {
addError(
errors,
pathText,
"pattern",
escapeRegExpError(schema.pattern, error.message),
data,
);
}
}
}
if (dataType === "object") {
if (Array.isArray(schema.required)) {
for (const key of schema.required) {
if (!Object.hasOwn(data, key)) {
addError(
errors,
`${pathText}.${key}`,
"required",
"field must exist",
undefined,
);
}
}
}
if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
for (const [key, subschema] of Object.entries(schema.properties)) {
if (Object.hasOwn(data, key)) {
validateAgainstSchema(data[key], subschema, `${pathText}.${key}`, errors);
}
}
}
}
if (dataType === "array") {
if (typeof schema.minItems === "number" && data.length < schema.minItems) {
addError(errors, pathText, "minItems", `items >= ${schema.minItems}`, data);
}
if (typeof schema.maxItems === "number" && data.length > schema.maxItems) {
addError(errors, pathText, "maxItems", `items <= ${schema.maxItems}`, data);
}
if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
data.forEach((item, index) => {
validateAgainstSchema(item, schema.items, `${pathText}[${index}]`, errors);
});
}
}
}
function generateSampleSchema() {
return {
type: "object",
required: ["users", "status", "metadata", "note"],
properties: {
users: {
type: "array",
minItems: 1,
items: {
type: "object",
required: ["name", "age", "email", "tags", "active"],
properties: {
name: { type: "string", minLength: 3, maxLength: 20 },
age: { type: "number", min: 0, max: 120 },
email: {
type: "string",
pattern: "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$",
},
tags: {
type: "array",
minItems: 1,
items: { type: "string", minLength: 2, maxLength: 12 },
},
active: { type: "boolean" },
},
},
},
status: { type: "string", enum: ["new", "active", "disabled"] },
metadata: {
type: "object",
required: ["version", "source"],
properties: {
version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
source: {
type: "object",
required: ["systemId"],
properties: {
systemId: { type: "string", minLength: 5 },
},
},
},
},
note: { type: "null" },
},
};
}
function generateSampleData() {
return {
users: [
{
name: "Alice",
age: 29,
email: "alice@example.com",
tags: ["owner", "ops"],
active: true,
},
{
name: "Al",
age: -5,
email: "invalid-email",
tags: ["ok", 5, ""],
active: "true",
},
{
name: 123,
age: 200,
tags: [],
active: false,
},
],
status: "archived",
metadata: {
version: "1.0",
source: { systemId: 42 },
},
note: "should be null",
};
}
async function loadJsonFile(filePath, label) {
let text;
try {
text = await fs.readFile(filePath, "utf8");
} catch (error) {
throw new Error(`${label} file read error (${filePath}): ${error.message}`);
}
try {
return JSON.parse(text);
} catch (error) {
throw new Error(`${label} JSON parse error (${filePath}): ${error.message}`);
}
}
function printViolations(errors) {
if (errors.length === 0) {
console.log("Validation succeeded: no violations found.");
return;
}
console.log(`Validation failed: ${errors.length} violation(s) found.`);
for (const error of errors) {
console.log(
`${error.path} | expected: ${error.rule} -> ${error.expected} | actual: ${error.actual}`,
);
}
}
async function run() {
const args = process.argv.slice(2);
let dataPath;
let schemaPath;
if (args.length === 0) {
dataPath = path.resolve(process.cwd(), "sample_data.json");
schemaPath = path.resolve(process.cwd(), "sample_schema.json");
await fs.writeFile(dataPath, `${JSON.stringify(generateSampleData(), null, 2)}\n`, "utf8");
await fs.writeFile(schemaPath, `${JSON.stringify(generateSampleSchema(), null, 2)}\n`, "utf8");
console.log(`No input files provided. Generated sample data: ${dataPath}`);
console.log(`No input files provided. Generated sample schema: ${schemaPath}`);
} else if (args.length === 2) {
dataPath = path.resolve(process.cwd(), args[0]);
schemaPath = path.resolve(process.cwd(), args[1]);
} else {
console.error("Usage: node src/index.js <data.json> <schema.json>");
console.error(" or: node src/index.js");
process.exitCode = 1;
return;
}
let data;
let schema;
try {
data = await loadJsonFile(dataPath, "Data");
schema = await loadJsonFile(schemaPath, "Schema");
} catch (error) {
console.error(error.message);
process.exitCode = 1;
return;
}
const errors = [];
validateAgainstSchema(data, schema, "$", errors);
printViolations(errors);
const report = {
timestamp: new Date().toISOString(),
dataFile: dataPath,
schemaFile: schemaPath,
valid: errors.length === 0,
errorCount: errors.length,
errors,
};
const reportPath = path.resolve(process.cwd(), "validation_report.json");
await fs.writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
console.log(`Saved structured report: ${reportPath}`);
}
run().catch((error) => {
console.error(`Unexpected failure: ${error.message}`);
process.exitCode = 1;
});