Skip to content

PascalCase / legacy schema files load as an empty schema reported as a successful migration; saving from the editor then wipes the file #278

Closed

Description

@matt-edmondson

What's wrong

SchemaSerializer only reads camelCase property names (Schema/SchemaSerializer.cs:16-26). It sets PropertyNamingPolicy = CamelCase, does not set PropertyNameCaseInsensitive, and does not reject unmapped members. So every key in a document written with Classes, Enums, Name, Members, ... is silently ignored.

Such a file also has no formatVersion, so Load treats it as version 0 and returns Status = Success with Message = "Migrated from format version 0 to 7." (SchemaSerializer.cs:~181-203). The resulting schema has 0 classes, 0 enums and 0 validation issues.

Reproduction

Every file under samples/legacy/** (26 files: the repository's own Carbon Monoxide and Dungeoneer schemas) produces this:

var r = SchemaSerializer.Load(File.ReadAllText("samples/legacy/dungeoneer/weapon.schema.json"));
// Success | Migrated from format version 0 to 7. | classes=0 enums=0 issues=0

A hand-written { "Classes": [ { "Name": "A", ... } ] } behaves the same way.

Consequences

  • kschema validate old.schema.json prints 0 error(s), 0 warning(s) and exits 0.
  • kschema generate reports "declares no code generators" rather than saying the file wasn't understood.
  • In the editor (Schema.Editor/SchemaEditor.Files.cs, LoadFrom), the file opens as an empty document with no message. Any edit followed by Save overwrites the original with an empty schema, and the data is lost.
  • docs/schema-format.md:708-709 says "No version field - treat it as version 0 and migrate. Files written before versioning are still readable and always will be." For these files that promise is broken.

Not supporting the legacy dialect is deliberate (samples/README.md; LegacySchemaReader lives in the tests). Loading it as an empty success is not.

Suggested fix

  • Refuse a document whose root has properties the model doesn't recognise. Either set JsonUnmappedMemberHandling.Disallow on Schema, or pre-check the known root keys in ReadFormatVersion. Return SchemaLoadStatus.InvalidJson with a message that names the key, e.g. "unrecognised property 'Classes' — property names are camelCase".
  • Or set PropertyNameCaseInsensitive = true, so that casing alone never drops data.
  • Either way, add a test that loading samples/legacy/dungeoneer/weapon.schema.json through SchemaSerializer.Load does not succeed with an empty schema.

Activity

  1. matt-edmondson commented on Oct 17, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug (silent data loss)
    • Priority: High. A PascalCase or legacy file loads as an empty schema reported as a successful migration. validate passes, and saving from the editor overwrites the user's file with an empty schema. All 26 samples/legacy/** files reproduce it.
    • Area / suggested assignment: Schema SchemaSerializer (load / migration), plus the editor's save path.
    • Duplicates / in progress: None found. The only open Schema PR is the Bump ktsu.ImGui.NodeEditor from 3.38.5 to 3.75.1 #277 dependency bump.
    • Next step: Refuse unmapped root properties with InvalidJson, and give a message naming the key (preferred over silently accepting any casing). Add the weapon.schema.json regression test. Optionally, have the editor refuse to save over a file that loaded with errors.

    Generated by Claude Code

  2. added a commit that references this issue on Oct 19, 2026
    b5684b5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions