What's wrong
JsonKeybindingRepository.LoadAllProfilesInternalAsync (Keybinding/Services/JsonKeybindingRepository.cs:~99-120) catches only JsonException. For a corrupted file it returns an empty list.
The DTO-to-model conversion inside the same try builds Profile, Note and Chord objects. Those constructors throw ArgumentException when the JSON is well-formed but the content is invalid:
- a chord with an empty
notes array
- a note string that is empty or whitespace
- a profile with an empty
id or name
That ArgumentException escapes LoadAllProfilesAsync, so KeybindingManager.InitializeAsync fails and the app can't start. SaveProfileAsync and DeleteProfileAsync reload the file first, so they throw the same exception and SaveAsync breaks too.
A hand-edited profile file is enough to hit this, and so is one written by an older version or partly migrated.
Reproduction (verified against the built library)
Write profiles.json in the data directory:
[{"id":"default","name":"Default","chords":{"file.save":{"notes":[]}}}]
Then run await new KeybindingManager(dir).InitializeAsync();.
- Actual:
ArgumentException: Chord must contain at least one note (Parameter 'notes') escapes InitializeAsync. If the file contains {not json instead, InitializeAsync succeeds, because the JsonException is swallowed.
- Expected: the same tolerance as the corrupted-file path. The invalid binding or profile is skipped, the rest of the file loads, and nothing crashes.
Suggested fix / acceptance criteria
- In the conversion, catch
ArgumentException for each entry:
- skip a chord entry whose notes are empty or invalid
- skip a profile whose id or name is invalid
- at minimum, catch
ArgumentException alongside JsonException
- Make sure a later
SaveProfileAsync doesn't silently drop the skipped entries without telling the caller. For example, log or surface them.
- Acceptance: the JSON above loads the
default profile without a file.save binding, and InitializeAsync doesn't throw. Add a test for it next to the existing corrupted-file test.
What's wrong
JsonKeybindingRepository.LoadAllProfilesInternalAsync(Keybinding/Services/JsonKeybindingRepository.cs:~99-120) catches onlyJsonException. For a corrupted file it returns an empty list.The DTO-to-model conversion inside the same
trybuildsProfile,NoteandChordobjects. Those constructors throwArgumentExceptionwhen the JSON is well-formed but the content is invalid:notesarrayidornameThat
ArgumentExceptionescapesLoadAllProfilesAsync, soKeybindingManager.InitializeAsyncfails and the app can't start.SaveProfileAsyncandDeleteProfileAsyncreload the file first, so they throw the same exception andSaveAsyncbreaks too.A hand-edited profile file is enough to hit this, and so is one written by an older version or partly migrated.
Reproduction (verified against the built library)
Write
profiles.jsonin the data directory:[{"id":"default","name":"Default","chords":{"file.save":{"notes":[]}}}]Then run
await new KeybindingManager(dir).InitializeAsync();.ArgumentException: Chord must contain at least one note (Parameter 'notes')escapesInitializeAsync. If the file contains{not jsoninstead,InitializeAsyncsucceeds, because theJsonExceptionis swallowed.Suggested fix / acceptance criteria
ArgumentExceptionfor each entry:ArgumentExceptionalongsideJsonExceptionSaveProfileAsyncdoesn't silently drop the skipped entries without telling the caller. For example, log or surface them.defaultprofile without afile.savebinding, andInitializeAsyncdoesn't throw. Add a test for it next to the existing corrupted-file test.