How to Troubleshoot DCG Hook Failures and Parse Errors in Destructive Command Guard
When you troubleshoot DCG hook failures and parse errors, you typically encounter either runtime exits with non-zero codes (hook-runtime failures) or malformed JSON outputs (parse errors), both of which originate in src/hook.rs and src/main.rs and can be diagnosed using RUST_LOG=debug and the --debug-session flag.
The Destructive Command Guard (DCG) from the Dicklesworthstone/destructive_command_guard repository functions as a Claude Code hook that evaluates commands through a JSON protocol read from stdin. When the binary fails to process inputs correctly, it produces specific error codes like DCG-3001 for JSON parse errors or exits silently due to runtime panics in the pattern matching engine. Understanding how to troubleshoot DCG hook failures and parse errors requires tracing the execution flow from ingestion through the evaluator pipeline.
Hook Execution Architecture and Failure Points
DCG processes commands through a strict pipeline defined across several core modules. The entry point in src/main.rs orchestrates the flow: first calling hook::parse_input in src/hook.rs to deserialize the JSON payload using serde_json::from_str, then passing the normalized command through src/normalize.rs for path stripping and alias expansion.
The quick-reject filter in src/evaluator.rs (utilizing memchr against the static QUICK_REJECT_SET) provides an early exit for safe commands. If the command passes this filter, DCG checks against whitelist patterns in the pattern! macro before evaluating destructive patterns defined in the destructive! macro. Finally, src/output.rs constructs the denial JSON via the HookSpecificOutput struct, writing structured output to stdout and colored warnings to stderr (controlled by colored::control::set_override in src/cli.rs).
Runtime failures typically occur when std::panic::catch_unwind in main.rs intercepts panics from downstream modules—most commonly regex compilation errors in src/evaluator.rs or I/O errors during pack loading.
Common DCG Hook Failure Patterns
DCG-3001: JSON Parse Errors
The DCG-3001 error code indicates that hook::parse_input failed to deserialize the incoming JSON. This occurs when the payload is missing required fields like tool_input.command or contains malformed syntax. The function returns an Err that propagates to main.rs, which prints an error JSON and exits with code 1.
Truncated or Malformed Denial Output
When the process aborts mid-write, you receive partial JSON output followed by a crash dump. This happens when an uncaught panic occurs after src/output.rs begins writing the denial but before the buffer flushes. Common causes include invalid regex patterns in the destructive! macro or corrupted pack files in src/packs/*.rs.
Silent Allows on Timeout
Commands containing complex heredocs may trigger the 200ms timeout in src/heredoc.rs. When heredoc::extract hits this deadline, it returns None, causing the hook to fail-open and silently allow the command without evaluation.
Configuration Loading Failures
DCG-2002 errors originate in src/config.rs when config::load fails to parse config.toml using toml_edit. Missing configuration files or syntax errors in user-defined packs trigger this error category.
TTY Detection Issues
If stderr is not a TTY, DCG disables colored warnings via colored::control::set_override, but still writes valid JSON to stdout. Callers that discard both streams may incorrectly assume the hook failed when it actually produced a denial.
Diagnostic Procedures for DCG Hooks
1. Enable Full Debug Logging
Run DCG with RUST_LOG=debug to surface tracing output from src/logging.rs. This reveals the exact processing stage where failures occur.
RUST_LOG=debug echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | dcg
Look for log lines tagged parse_input, normalize, evaluator, and any error entries indicating where the pipeline breaks.
2. Capture Session State with Debug Mode
The --debug-session flag triggers src/session.rs to write a dcg.session.json file containing the raw input, normalized command, and matched rule ID.
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
| dcg --debug-session > /dev/null
cat dcg.session.json
This file reveals whether the command reached the destructive-pattern engine and which specific rule triggered the denial.
3. Validate Input Against Test Schemas
Verify your JSON payload against the reference implementation in tests/codex_hook_protocol.rs, specifically the test_input_parsing test. Common mistakes include stray commas, single quotes instead of double, or missing nested fields under tool_input.
4. Inspect Stack Traces for Pack Errors
When adding custom packs, malformed YAML can cause panics. Run with RUST_BACKTRACE=1 to identify the specific location in src/packs/*.rs causing the failure.
5. Check Quick-Reject Filter Behavior
Review the QUICK_REJECT_SET definition in src/evaluator.rs if commands containing unexpected null bytes or binary data bypass evaluation entirely, leading to silent allows.
Practical Troubleshooting Examples
Reproducing a DCG-3001 JSON Parse Error
Send a payload missing the required command field to trigger the deserialization failure:
echo '{"tool_name":"Bash","tool_input":{}}' | RUST_LOG=debug dcg
Expected Output:
Log: ERROR hook::parse_input: JSON parse error: missing field 'command'
Exit Code: 1
JSON Output:
{
"error": {
"code": "DCG-3001",
"category": "runtime",
"message": "JSON parse error: missing field `command`",
"context": {}
}
}
Diagnosing Truncated JSON from Regex Panics
Introduce an invalid regex (e.g., (?<unclosed) in the destructive! macro within src/evaluator.rs, then rebuild:
cargo build --release
echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | dcg
Result: The process aborts mid-write, producing truncated output like { "hookSpecificOutput": { "hookEventName": "PreToolUse", followed by a panic dump. Correct the regex syntax and rebuild to resolve.
Using Debug Session Files
Generate a complete session trace to verify rule matching:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
| dcg --debug-session > /dev/null
cat dcg.session.json
Sample Output:
{
"raw_input": "...",
"normalized": "rm -rf /",
"matched_rule": {
"rule_id": "core.filesystem:rm-rf-root",
"severity": "critical"
},
"decision": "deny"
}
This confirms the command successfully reached the evaluator and was blocked by the specific filesystem rule.
Key Source Files for Troubleshooting
| File | Role | Critical Functions/Items |
|---|---|---|
src/main.rs |
Entry point and panic handling | std::panic::catch_unwind, CLI wiring |
src/hook.rs |
JSON ingestion and validation | hook::parse_input, serde_json::from_str |
src/evaluator.rs |
Pattern matching engine | destructive! macro, pattern! macro, QUICK_REJECT_SET |
src/normalize.rs |
Command preprocessing | Path stripping, alias expansion |
src/output.rs |
Denial JSON construction | HookSpecificOutput struct |
src/session.rs |
Debug state persistence | Session JSON serialization |
src/config.rs |
Configuration loading | config::load, toml_edit dependency |
src/heredoc.rs |
Heredoc extraction | 200ms timeout handling |
src/cli.rs |
TTY and color control | colored::control::set_override |
tests/codex_hook_protocol.rs |
Input validation tests | test_input_parsing |
Summary
- DCG-3001 errors indicate JSON deserialization failures in
src/hook.rs, typically from missingtool_input.commandfields or malformed syntax. - Runtime failures with non-zero exits or truncated output result from panics caught by
main.rs, often originating insrc/evaluator.rsregex compilation orsrc/config.rsloading. - Silent allows may indicate heredoc timeouts in
src/heredoc.rsor quick-reject filter matches insrc/evaluator.rs. - Use
RUST_LOG=debugfor real-time tracing and--debug-sessionto capture intermediate processing state indcg.session.json. - Reference
tests/codex_hook_protocol.rsfor valid JSON schema examples when constructing hook payloads.
Frequently Asked Questions
Why does DCG exit with code 1 and produce no stdout?
This indicates a hook-runtime failure where main.rs catches a panic via std::panic::catch_unwind or encounters an I/O error before JSON serialization completes. Enable RUST_LOG=debug to identify whether the failure occurs during JSON parsing in src/hook.rs or configuration loading in src/config.rs (DCG-2002).
How do I fix malformed JSON denials that crash my parser?
Malformed output usually stems from uncaught panics after src/output.rs begins writing the denial JSON but before the buffer flushes. Check src/evaluator.rs for invalid regex patterns in the destructive! macro, and ensure any custom packs in src/packs/*.rs contain valid YAML syntax. Running with RUST_BACKTRACE=1 reveals the specific panic location.
Why is my destructive command being allowed when it should be blocked?
First, verify the command is not matching a whitelist pattern in the pattern! macro within src/evaluator.rs. If the command contains complex heredocs, check if the 200ms timeout in src/heredoc.rs is causing a fail-open. Alternatively, the command may contain binary data that triggers the QUICK_REJECT_SET filter, causing early exit. Use --debug-session to confirm whether the evaluator ever processed the command.
How do I enable detailed session tracking for debugging?
Run DCG with the --debug-session flag to generate a dcg.session.json file via src/session.rs. This file contains the raw stdin input, normalized command string, matched rule ID (if any), and final decision. Combine this with RUST_LOG=debug to correlate internal processing steps with the final output state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →