Hmbown/CodeWhale · error

expected explicit block

Error message

expected explicit block

What it means

Test assertion message from explicit_message_denial_never_copies_raw_process_diagnostics: the hook executor was expected to emit an explicit structured denial block (sanitized reason) but did not. The test verifies that when a hook command denies a message submit, raw process diagnostics (stdout/stderr containing secrets like tokens and private paths) are never copied into the denial payload; this error means sanitization/structure regressed.

Solutions

  1. Run the test and inspect the actual denial payload produced by the HookExecutor
  2. Ensure the denial path emits an explicit block with the hook's stated reason only
  3. Verify stdout/stderr of the denied process are not forwarded verbatim (the fixture plants a token and private paths to catch leaks)
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at crates/tui/src/hooks/executor.rs:5583 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/30493c0a15c16faa. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/hooks/executor.rs:5583

        assert_eq!(payload["turn_id"], "turn_test");
    }

    #[cfg(unix)]
    #[test]
    fn explicit_message_denial_never_copies_raw_process_diagnostics() {
        let dir = tempfile::tempdir().expect("tempdir");
        let command = r#"printf '%s\n' '{"reason":"blocked /Users/alice/private --run token=SUPERSECRET"}'; printf '%s\n' 'stderr-secret /tmp/private' >&2; exit 2"#;
        let executor = HookExecutor::new(
            HooksConfig {
                enabled: true,
                hooks: vec![Hook::new(HookEvent::MessageSubmit, command)],
                ..HooksConfig::default()
            },
            dir.path().to_path_buf(),
        );
        let outcome = executor.execute_message_submit_transform(&HookContext::new(), "hello");
        let MessageSubmitOutcome::Blocked { reason } = outcome else {
            panic!("expected explicit block");
        };
        assert_eq!(reason, "blocked [path] [argument] [secret]");
        for secret in ["alice", "SUPERSECRET", "stderr-secret", "/tmp/private"] {
            assert!(!reason.contains(secret), "leaked {secret}: {reason}");
        }
    }

    #[cfg(unix)]
    #[test]
    fn foreground_pipe_capture_is_bounded_while_verbose_child_is_drained() {
        let hook = Hook::new(
            HookEvent::SessionStart,
            "head -c 200000 /dev/zero | tr '\\0' o; head -c 200000 /dev/zero | tr '\\0' e >&2",
        )
        .with_timeout(5);
        let executor = HookExecutor::new(HooksConfig::default(), PathBuf::from("."));
        let result = executor.execute_sync(&hook, &HashMap::new());
        assert!(result.success, "{:?}", result.error);

View on GitHub (pinned to 433685b202)