Skip to content

[go-fan] Go Module Review: santhosh-tekuri/jsonschema/v6Β #9380

Description

@github-actions

🐹 Go Fan Report: santhosh-tekuri/jsonschema/v6

Module Overview

github.com/santhosh-tekuri/jsonschema/v6 is a Go implementation of JSON Schema validation supporting Draft 4, Draft 6, Draft 7, Draft 2019-09, and Draft 2020-12 β€” the latest and most capable dialect. It is used in this project to validate MCP Gateway configuration against a bundled JSON Schema document at startup and at request time.

Current Usage in gh-aw

  • Files: 2 production files + 4 test files
  • Import Count: 6 imports (including kind sub-package)
  • Key APIs Used:
    • jsonschema.NewCompiler() β€” creates a Draft 2020-12 compiler
    • jsonschema.UnmarshalJSON() β€” precision-safe JSON parsing
    • compiler.AddResource() β€” registers schemas by URL
    • compiler.Compile() β€” compiles a schema for reuse
    • schema.Validate() β€” validates a parsed document
    • *jsonschema.ValidationError + ve.Causes, ve.InstanceLocation, ve.ErrorKind β€” rich error tree traversal
    • kind.* types (AdditionalProperties, Type, Enum, Required, Pattern, OneOf, etc.) β€” typed error dispatch
    • ErrorKind.LocalizedString(printer) β€” i18n-aware error messages via golang.org/x/text

Usage Patterns

Pattern Status
Embedded schema via go:embed βœ… Implemented
sync.Once compile-once caching βœ… Implemented
jsonschema.UnmarshalJSON for number precision βœ… Implemented
Type-switch on kind.* for error dispatch βœ… Implemented
Recursive ve.Causes traversal βœ… Implemented
Localized error messages βœ… Implemented
Remote schema caching via sync.Map βœ… Implemented
HTTPS enforcement for remote schemas βœ… Implemented
IO size-bounded remote fetch (io.LimitReader) βœ… Implemented

Research Findings

The project makes excellent, idiomatic use of this library. It follows the recommended patterns from the upstream README:

  • Use UnmarshalJSON (not encoding/json) to avoid number precision issues
  • Compile schemas once and reuse (the project uses sync.Once)
  • Use the kind sub-package for typed error introspection

Best Practices in Use

  • The compiler defaults to Draft 2020-12 (the library default since v6), which is the most capable dialect
  • The embedded schema eliminates network dependency at startup
  • Both the compiled schema and compilation errors are cached so failed compilations are not retried endlessly

Improvement Opportunities

πŸƒ Quick Wins

1. Stale draft version hint in error message

In internal/config/validation_server.go line 305, a compile-failure error message says:

"The schema at '%s' must be a valid JSON Schema Draft 7 document"

But the compiler is created via jsonschema.NewCompiler() which defaults to Draft 2020-12, not Draft 7. This misleads users when their remote custom schema fails to compile.

Fix: Update the hint to say "valid JSON Schema document (Draft 2020-12 or earlier)" or simply "valid JSON Schema document".

2. fmt.Errorf("%s", sb.String()) anti-pattern

In internal/config/validation_schema.go line 252:

return fmt.Errorf("%s", sb.String())

This is a misuse of fmt.Errorf when there is no actual %w error wrapping and no format verbs on an error value. Prefer:

return errors.New(sb.String())

This avoids the go vet / golangci-lint suggestion about using errors.New for static string errors and is more idiomatic.

✨ Feature Opportunities

1. Schema draft auto-detection for custom schemas

The library supports multiple drafts β€” if custom schemas submitted by users target Draft 7 (a common default for many toolchains), they may fail compilation or produce confusing errors under the Draft 2020-12 compiler. Consider documenting the expected draft version in error messages or calling compiler.SetDefaultDraft() when compiling custom (remote) schemas.

2. Structured error output for machine consumers

Currently formatSchemaError returns a human-readable string. For programmatic clients (e.g., Copilot agents consuming config validation errors), a structured JSON error representation would be more useful. The ValidationError tree is already available and could be serialized alongside the human-readable message.

3. Expand formatErrorContext for additional kind.* types

The kind sub-package includes more constraint types than are currently handled (e.g., UniqueItems, Contains, ContentMediaType, If/Then/Else). Periodic review of new kind.* additions could improve error guidance for users hitting those constraints.

πŸ“ Best Practice Alignment

1. Number precision in validateServerAgainstSchema

In validateServerAgainstSchema, the server config is validated via json.Marshal β†’ json.Unmarshal into map[string]interface{} before passing to schema.Validate. This round-trip could theoretically lose number precision for large integers or high-precision floats. For consistency with the embedded schema validation path, consider using jsonschema.UnmarshalJSON on the marshalled bytes instead of encoding/json.Unmarshal.

// Current:
json.Unmarshal(serverJSON, &serverMap)

// Suggested:
serverObj, parseErr := jsonschema.UnmarshalJSON(bytes.NewReader(serverJSON))
// then use serverObj directly in schema.Validate()

2. newCompiler() options for extensibility

Currently newCompiler() takes no parameters. If draft pinning or vocabulary configuration is ever needed (e.g., to handle legacy Draft 7 custom schemas), accepting functional options would make this easy to extend without breaking callers.

πŸ”§ General Improvements

1. Consider compiler.UseLoader for remote schema management

The library supports a UseLoader mechanism for pluggable resource loading. The current implementation manually fetches remote schemas (with retries, size limits) and manages a sync.Map cache. Using UseLoader could centralize the HTTP fetch + caching logic, make it easier to test with mock loaders, and align with library conventions.

2. Test coverage for the unknown ErrorKind default path

The switch ve.ErrorKind.(type) in formatErrorContext has an implicit default no-op path for unrecognised kind.* types (returning an empty string). There is no test verifying this behavior. A simple test case would catch any future regression if an unknown kind accidentally matched a case.

Module Summary

Field Value
Module github.com/santhosh-tekuri/jsonschema/v6
Version v6.0.2
Repository https://github.com/santhosh-tekuri/jsonschema
Default Draft 2020-12
Last Reviewed 2026-07-15

Key Features

  • Full Draft 2020-12 support (backward compatible with Draft 4–2019-09)
  • UnmarshalJSON for precision-safe JSON parsing
  • Typed kind.* error introspection sub-package
  • Localized error messages via ErrorKind.LocalizedString(printer)
  • AddResource / Compile for schema composition and $ref resolution
  • Thread-safe compilation model (compile once, validate many times)

References

Recommendations

  1. Fix the misleading Draft 7 error hint in validateAgainstCustomSchema (validation_server.go:305) β€” change to "valid JSON Schema document (Draft 2020-12 or earlier)" (trivial, high clarity value)
  2. Replace fmt.Errorf("%s", sb.String()) with errors.New(sb.String()) in formatSchemaError (validation_schema.go:252) (trivial cleanup)
  3. Add a test for the unknown ErrorKind default path in TestFormatErrorContext (low effort, prevents regression)
  4. Use jsonschema.UnmarshalJSON in validateServerAgainstSchema for number-precision consistency with the embedded schema path (medium effort, improves correctness)
  5. Evaluate compiler.UseLoader as a potential simplification for the remote schema fetch/cache flow (future enhancement)

Next Steps

  • Fix the Draft 7 hint text in validation_server.go:305
  • Replace fmt.Errorf("%s", ...) with errors.New() in validation_schema.go:252
  • Add a test for the unknown-kind default path in TestFormatErrorContext
  • Explore compiler.UseLoader as a potential simplification

Generated by Go Fan 🐹 · Run §29398122955

References:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Go Fan Β· 101.4 AIC Β· ⊞ 6.8K Β· β—·

  • expires on Jul 22, 2026, 7:48 AM UTC

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions