# How to Troubleshoot DCG Hook Failures and Parse Errors in Destructive Command Guard

> Troubleshoot DCG hook failures and parse errors effectively. Learn to diagnose runtime exits and malformed JSON with RUST_LOG=debug and --debug-session flags.

- Repository: [Jeff Emanuel/destructive_command_guard](https://github.com/Dicklesworthstone/destructive_command_guard)
- Tags: how-to-guide
- Published: 2026-07-14

---

**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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs) and [`src/main.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs) orchestrates the flow: first calling `hook::parse_input` in [`src/hook.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs) to deserialize the JSON payload using `serde_json::from_str`, then passing the normalized command through [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) for path stripping and alias expansion.

The **quick-reject filter** in [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/cli.rs)).

Runtime failures typically occur when `std::panic::catch_unwind` in [`main.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/main.rs) intercepts panics from downstream modules—most commonly regex compilation errors in [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) when `config::load` fails to parse [`config.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/logging.rs). This reveals the exact processing stage where failures occur.

```bash
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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/session.rs) to write a [`dcg.session.json`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/dcg.session.json) file containing the raw input, normalized command, and matched rule ID.

```bash
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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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:

```bash
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:*

```json
{
  "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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs), then rebuild:

```bash
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:

```bash
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
| dcg --debug-session > /dev/null
cat dcg.session.json

```

**Sample Output:**

```json
{
  "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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs) | Entry point and panic handling | `std::panic::catch_unwind`, CLI wiring |
| [`src/hook.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs) | JSON ingestion and validation | `hook::parse_input`, `serde_json::from_str` |
| [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs) | Pattern matching engine | `destructive!` macro, `pattern!` macro, `QUICK_REJECT_SET` |
| [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) | Command preprocessing | Path stripping, alias expansion |
| [`src/output.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/output.rs) | Denial JSON construction | `HookSpecificOutput` struct |
| [`src/session.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/session.rs) | Debug state persistence | Session JSON serialization |
| [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) | Configuration loading | `config::load`, `toml_edit` dependency |
| [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs) | Heredoc extraction | 200ms timeout handling |
| [`src/cli.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/cli.rs) | TTY and color control | `colored::control::set_override` |
| [`tests/codex_hook_protocol.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/tests/codex_hook_protocol.rs) | Input validation tests | `test_input_parsing` |

## Summary

- **DCG-3001 errors** indicate JSON deserialization failures in [`src/hook.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs), typically from missing `tool_input.command` fields or malformed syntax.
- **Runtime failures** with non-zero exits or truncated output result from panics caught by [`main.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/main.rs), often originating in [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs) regex compilation or [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/config.rs) loading.
- **Silent allows** may indicate heredoc timeouts in [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/heredoc.rs) or quick-reject filter matches in [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs).
- Use `RUST_LOG=debug` for real-time tracing and `--debug-session` to capture intermediate processing state in [`dcg.session.json`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/dcg.session.json).
- Reference [`tests/codex_hook_protocol.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/tests/codex_hook_protocol.rs) for 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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/hook.rs) or configuration loading in [`src/config.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/output.rs) begins writing the denial JSON but before the buffer flushes. Check [`src/evaluator.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs). If the command contains complex heredocs, check if the 200ms timeout in [`src/heredoc.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/dcg.session.json) file via [`src/session.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/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.