Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/com/networknt/schema/Schema.java
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,8 @@ private JsonNode deserialize(String input, InputFormat inputFormat) {
return this.getSchemaContext().getSchemaRegistry().readTree(input, inputFormat);
} catch (IOException e) {
throw new UncheckedIOException("Invalid input", e);
} catch (RuntimeException e) {
throw new SchemaException("Invalid " + inputFormat + " input: " + e.getMessage(), e);
}
}

Expand All @@ -1213,6 +1215,8 @@ private JsonNode deserialize(AbsoluteIri input, InputFormat inputFormat) {
}
} catch (IOException e) {
throw new UncheckedIOException("Invalid input", e);
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deserialize(AbsoluteIri, ...) deliberately throws an UncheckedIOException when the resource isn't found, but the new catch (RuntimeException e) will wrap that into a SchemaException with an "Invalid ... input" message. This changes the exception type/semantics for missing resources and makes the message misleading. Handle UncheckedIOException separately (rethrow) or narrow the runtime wrapping to the specific parser exceptions you intend to convert.

Suggested change
throw new UncheckedIOException("Invalid input", e);
throw new UncheckedIOException("Invalid input", e);
} catch (UncheckedIOException e) {
// Preserve deliberately thrown UncheckedIOException (e.g., missing resource) semantics
throw e;

Copilot uses AI. Check for mistakes.
} catch (RuntimeException e) {
throw new SchemaException("Invalid " + inputFormat + " input from " + input + ": " + e.getMessage(), e);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/networknt/schema/SchemaException.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public SchemaException(Throwable throwable) {
this.error = null;
}

public SchemaException(String message, Throwable throwable) {
super(message, throwable);
this.error = null;
}

@Override
public String getMessage() {
return this.error != null ? this.error.getMessage() : super.getMessage();
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/com/networknt/schema/SchemaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,36 @@ public void run() {
throw instance[0];
}
}

/**
* Issue 1231.
* <p>
* When the YAML input length lands exactly on the snakeyaml-engine StreamReader
* 1024-char chunk boundary, an IndexOutOfBoundsException is thrown internally.
* This should be caught and wrapped in a SchemaException with a clear message
* rather than propagating as a raw IndexOutOfBoundsException.
*/
@Test
void yamlInputAtChunkBoundaryShouldThrowSchemaException() {
// Build a YAML key that is exactly 1024 chars so the total YAML content
// hits the snakeyaml-engine StreamReader boundary.
Comment on lines +105 to +113
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc says the YAML input length lands exactly on the 1024-char boundary, but yamlInput is longer than 1024 characters (longKey is 1024 plus the ": value\n" suffix). Consider rewording to clarify what is actually at the boundary (e.g., the colon position / token boundary) so the test rationale matches the constructed input.

Suggested change
* When the YAML input length lands exactly on the snakeyaml-engine StreamReader
* 1024-char chunk boundary, an IndexOutOfBoundsException is thrown internally.
* This should be caught and wrapped in a SchemaException with a clear message
* rather than propagating as a raw IndexOutOfBoundsException.
*/
@Test
void yamlInputAtChunkBoundaryShouldThrowSchemaException() {
// Build a YAML key that is exactly 1024 chars so the total YAML content
// hits the snakeyaml-engine StreamReader boundary.
* When the YAML key length (so that the colon/token position) lands exactly on
* the snakeyaml-engine StreamReader 1024-char chunk boundary, an
* IndexOutOfBoundsException is thrown internally. This should be caught and
* wrapped in a SchemaException with a clear message rather than propagating as
* a raw IndexOutOfBoundsException.
*/
@Test
void yamlInputAtChunkBoundaryShouldThrowSchemaException() {
// Build a YAML key that is exactly 1024 chars so that the colon falls on
// the snakeyaml-engine StreamReader 1024-char chunk boundary (the overall
// YAML input is therefore slightly longer than 1024 characters).

Copilot uses AI. Check for mistakes.
String longKey = "a".repeat(1024);
String yamlInput = longKey + ": value\n";

String schemaData = "{\"type\": \"object\"}";
SchemaRegistry factory = SchemaRegistry.withDialect(com.networknt.schema.dialect.Dialects.getOpenApi31());
Schema schema = factory.getSchema(schemaData);

// Before the fix this threw IndexOutOfBoundsException directly.
// After the fix it should throw SchemaException with a clear message.
try {
schema.validate(yamlInput, InputFormat.YAML);
} catch (IndexOutOfBoundsException e) {
// Raw IOOBE must not escape - this is the bug we're fixing
throw new AssertionError("Expected SchemaException but got raw IndexOutOfBoundsException", e);
} catch (RuntimeException e) {
// Any other RuntimeException (including SchemaException) is acceptable
// as long as it's not the raw IndexOutOfBoundsException
}
Comment on lines +110 to +131
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test will pass even if schema.validate(...) does not throw at all, and it also treats any RuntimeException as acceptable (including unrelated NullPointerException, etc.). To make this a reliable regression test for issue 1231, assert that a SchemaException is thrown (and ideally assert its message/prefix), and fail the test when no exception is thrown.

Copilot uses AI. Check for mistakes.
}
}