Good news: ClaudeEssentials handles all of the serialization quirks documented below automatically.
You don’t need to worry about these details when using our library.This guide exists for two reasons:
- Transparency - So you understand what’s happening under the hood when debugging issues
- Advocacy - To document these inconsistencies so Anthropic can address them in future versions of Claude Code
Property Naming: The snake_case / camelCase Split
One of the most surprising aspects of Claude Code hooks is that inputs and outputs use different naming conventions.Hook Inputs: snake_case
All properties in hook input payloads from Claude Code usesnake_case:
ClaudeEssentials handles this automatically with
[JsonPropertyName("snake_case")] attributes on all input types.Hook Outputs: camelCase
When your hook returns a response to Claude Code, all properties must usecamelCase:
Enum Value Casing: The Inconsistent Trio
Claude Code expects different casing for different enum types. Getting these wrong results inJsonException errors during deserialization. ClaudeEssentials uses [JsonStringEnumMemberName]
attributes to handle each case correctly.
HookEventName: PascalCase
Thehook_event_name property uses PascalCase values:
PreToolUsePostToolUsePermissionRequestUserPromptSubmitNotificationStopSubagentStopPreCompactSessionStartSessionEnd
PermissionMode: camelCase
Thepermission_mode property uses camelCase values:
defaultplanacceptEditsbypassPermissions
PermissionDecision: lowercase
ThepermissionDecision property in hook outputs uses all lowercase:
allowdenyask
Quick Reference Table
| Context | Convention | Example |
|---|---|---|
| Input property names | snake_case | hook_event_name, tool_input, permission_mode |
| Output property names | camelCase | hookSpecificOutput, permissionDecision, stopReason |
| HookEventName values | PascalCase | PreToolUse, PostToolUse, SessionStart |
| PermissionMode values | camelCase | default, acceptEdits, bypassPermissions |
| PermissionDecision values | lowercase | allow, deny, ask |
Common Errors and Solutions
These are errors you might see if building your own serialization, or issues ClaudeEssentials prevents:“The JSON value could not be converted to PermissionMode”
Cause: Using wrong casing forpermission_mode value.
Solution: Use camelCase: "acceptEdits" not "AcceptEdits".
ClaudeEssentials fix: [JsonStringEnumMemberName("acceptEdits")] on enum values.
”The JSON value could not be converted to PermissionDecision”
Cause: Using wrong casing forpermissionDecision value in output.
Solution: Use lowercase: "allow" not "Allow".
ClaudeEssentials fix: [JsonStringEnumMemberName("allow")] on enum values.
Hook output has no effect
Cause: Using snake_case for output properties. Solution: Use camelCase:"hookSpecificOutput" not "hook_specific_output".
ClaudeEssentials fix: [JsonPropertyName("hookSpecificOutput")] on output properties.
Summary
ClaudeEssentials Handles It
All naming conventions and enum casing are handled automatically through JSON attributes. Just use the library types.
AOT Compatible
All serialization uses source generators and
JsonTypeInfo<T> for ahead-of-time compilation support.Tested Against Real Payloads
Our test suite uses actual Claude Code hook payloads captured from production usage.
For Anthropic: We hope these inconsistencies can be addressed in a future version of Claude Code to
provide a more consistent developer experience. Specifically:
- Standardizing on a single casing convention for both inputs and outputs
- Standardizing enum value casing across all enum types