1
00:00:00,000 --> 00:00:01,440
Here's a fun one to start the day.

2
00:00:01,680 --> 00:00:06,880
You write a hook to stop Claude Code
running recursive force deletes,

3
00:00:06,920 --> 00:00:10,080
you point it at a script, and you've got a
typo in the path.

4
00:00:10,220 --> 00:00:11,960
Nothing breaks.

5
00:00:11,995 --> 00:00:13,440
Nothing warns you loudly.

6
00:00:13,480 --> 00:00:15,040
The delete just runs.

7
00:00:15,184 --> 00:00:19,920
This is The Claude Code Changelog, brought
to you by Jellypod AI,

8
00:00:19,960 --> 00:00:22,240
and today's the fix for exactly that.

9
00:00:22,250 --> 00:00:27,850
Version 2.1.295, released October 8th,
adds a setting called onFailure,

10
00:00:27,910 --> 00:00:31,450
set to "block", for command and HTTP
hooks.

11
00:00:31,570 --> 00:00:36,570
The changelog says a hook that can't
start, times out, or exits with an unexpected

12
00:00:36,698 --> 00:00:39,690
code blocks the action instead of letting
it through.

13
00:00:39,840 --> 00:00:45,930
Version 2.1.294 shipped earlier the same
day with two hook fixes,

14
00:00:45,950 --> 00:00:47,130
and we'll get to one of those.

15
00:00:47,167 --> 00:00:49,287
So let's be clear on the old behaviour.

16
00:00:49,377 --> 00:00:53,887
For events that can block, like
PreToolUse, UserPromptSubmit,

17
00:00:54,015 --> 00:00:58,927
Stop and PreCompact, only exit code 2
halts anything.

18
00:00:59,135 --> 00:01:05,647
Exit 1, which every Unix person reads as
failure, is treated as a nonblocking error.

19
00:01:05,787 --> 00:01:10,927
Missing script, missing jq, crash,
timeout, same story.

20
00:01:11,007 --> 00:01:13,167
Claude Code logs it and carries on.

21
00:01:13,527 --> 00:01:17,087
A guard that's broken looks identical to a
guard that's quiet.

22
00:01:17,125 --> 00:01:19,365
And that was a real gap in the docs too.

23
00:01:19,477 --> 00:01:24,485
Anthropic's hooks reference said a timed
out hook doesn't block the tool call,

24
00:01:24,605 --> 00:01:28,005
and told you not to count on a stalled
hook as a gate.

25
00:01:28,285 --> 00:01:32,485
A write up from Mixed News on October 9th
noted that the reference page didn't

26
00:01:32,535 --> 00:01:35,125
mention onFailure at all when they read it
on the 8th.

27
00:01:35,285 --> 00:01:38,405
So if you go looking and can't find it in
the docs, that's why.

28
00:01:38,505 --> 00:01:40,965
The release notes are the source of truth
right now.

29
00:01:41,000 --> 00:01:41,880
Here's the config.

30
00:01:42,173 --> 00:01:48,520
In your settings dot json, under hooks,
PreToolUse, matcher Bash,

31
00:01:48,640 --> 00:01:50,280
you've got a hook object.

32
00:01:50,472 --> 00:01:52,840
Type is command.

33
00:01:52,920 --> 00:01:59,400
Command is your project dir variable, then
slash dot claude slash hooks

34
00:01:59,533 --> 00:02:02,200
slash guard dot sh.

35
00:02:02,424 --> 00:02:08,680
Then two extra fields: timeout set to 5,
and onFailure set to

36
00:02:08,800 --> 00:02:09,560
"block".

37
00:02:09,583 --> 00:02:15,743
Developers Digest tested this on 2.1.295
with six runs of the same request,

38
00:02:15,791 --> 00:02:17,023
just running an echo.

39
00:02:17,103 --> 00:02:20,943
Script path doesn't exist, no onFailure:
the echo ran.

40
00:02:21,039 --> 00:02:24,143
Same missing path with onFailure block:
blocked.

41
00:02:24,343 --> 00:02:28,143
A script that sleeps thirty seconds with a
three second timeout: runs without the

42
00:02:28,193 --> 00:02:29,503
setting, blocked with it.

43
00:02:29,783 --> 00:02:33,343
A script hitting a jq error and exiting 1:
blocked.

44
00:02:33,483 --> 00:02:36,703
And a healthy guard with a safe command
still let it through.

45
00:02:36,863 --> 00:02:41,023
So the setting changes what a broken hook
does, not what a working one does.

46
00:02:41,042 --> 00:02:42,402
The messages are useful too.

47
00:02:42,502 --> 00:02:46,962
You get something like: PreToolUse Bash
hook error, the script path,

48
00:02:47,042 --> 00:02:50,002
failed, blocking because onFailure is
"block".

49
00:02:50,182 --> 00:02:53,282
For a timeout it says timed out, same
ending.

50
00:02:53,495 --> 00:02:57,602
So Claude reports back why it stopped, and
you're not left guessing.

51
00:02:57,625 --> 00:02:59,465
Now the caveats, and there are a few.

52
00:02:59,612 --> 00:03:01,785
First, it's strictly opt in.

53
00:03:01,895 --> 00:03:06,745
Nothing on Anthropic's pages says the
default changed, so every hook you wrote last

54
00:03:06,798 --> 00:03:09,545
month still fails open until you add the
line.

55
00:03:09,682 --> 00:03:11,625
Second, the timeout trap.

56
00:03:11,765 --> 00:03:16,345
The default timeout for command and HTTP
hooks is 600 seconds.

57
00:03:16,625 --> 00:03:21,305
A hung guard on an unattended session sits
there for ten minutes before it fails,

58
00:03:21,417 --> 00:03:22,825
open or closed.

59
00:03:22,953 --> 00:03:25,545
That's why the example has timeout 5.

60
00:03:25,865 --> 00:03:28,105
A shell guard needs a few seconds at most.

61
00:03:28,125 --> 00:03:29,165
Third, scope.

62
00:03:29,352 --> 00:03:32,525
It covers command and HTTP hooks only.

63
00:03:32,662 --> 00:03:38,845
Prompt, agent and MCP tool hooks aren't
covered, and Developers Digest points out

64
00:03:38,985 --> 00:03:43,325
MCP tool hooks still give a nonblocking
error if the server's disconnected.

65
00:03:43,462 --> 00:03:47,805
Fourth, it only matters on events where
exit 2 already blocks.

66
00:03:48,018 --> 00:03:52,765
Slap it on a PostToolUse hook and, well,
the tool's already done its thing.

67
00:03:52,792 --> 00:03:54,632
Fifth is one for CI people.

68
00:03:54,779 --> 00:04:00,472
Classmethod tested headless mode with the
p flag, and the outer claude process still

69
00:04:00,552 --> 00:04:03,592
exited 0 even when the hook blocked the
action.

70
00:04:03,752 --> 00:04:06,072
So don't rely on the shell's exit status.

71
00:04:06,099 --> 00:04:07,832
Check the output for the block message.

72
00:04:07,972 --> 00:04:12,792
And one more subtle point from the
Developers Digest tests: if your script itself is

73
00:04:12,885 --> 00:04:17,992
naive, say jq is missing so a variable
comes out empty and your regex never matches,

74
00:04:18,092 --> 00:04:19,832
the script exits 0.

75
00:04:19,976 --> 00:04:23,672
From Claude's side the hook succeeded, so
onFailure can't help.

76
00:04:23,852 --> 00:04:30,392
Use set dash e, u, o pipefail and a trap
that exits 2, if you want belt and braces.

77
00:04:30,417 --> 00:04:31,777
Smaller things in these releases.

78
00:04:31,817 --> 00:04:32,417
Quick fire.

79
00:04:32,497 --> 00:04:38,257
Version 2.1.294 fixed prompt and agent
hooks written as instructions,

80
00:04:38,289 --> 00:04:42,257
like "Block commands that...", which were
allowing what they should block.

81
00:04:42,364 --> 00:04:44,258
Worth rechecking if you use those.

82
00:04:44,401 --> 00:04:49,857
Terminals that support OSC 7501, the
Program Status Protocol,

83
00:04:49,897 --> 00:04:53,777
can now show whether Claude is working,
waiting on you, or done,

84
00:04:53,884 --> 00:04:55,537
handy across tabs.

85
00:04:55,649 --> 00:04:59,937
Headless mode now prints one line to
stderr in a terminal saying what it's waiting

86
00:04:59,997 --> 00:05:02,577
for if it stays open after the last turn.

87
00:05:02,583 --> 00:05:06,423
The copy picker now copies quoted text
without the greater than markers.

88
00:05:06,503 --> 00:05:08,263
There's a new environment variable,

89
00:05:08,316 --> 00:05:15,303
CLAUDE_CODE_RETRY_WATCHDOG_MAX_WAIT_MS,
capping how long

90
00:05:15,383 --> 00:05:19,943
unattended retry mode waits out 429 and
529 errors.

91
00:05:20,083 --> 00:05:25,063
And requests on one million context models
now retry without the beta flag when a

92
00:05:25,123 --> 00:05:31,143
gateway, Bedrock, Vertex or Foundry
refuses it, instead of every request failing.

93
00:05:31,167 --> 00:05:35,407
So the takeaway: audit your hooks that
exist to say no.

94
00:05:35,627 --> 00:05:42,207
Add onFailure block, add a short timeout,
and run claude version to make sure

95
00:05:42,500 --> 00:05:43,727
you're on 2.1.295 or later.

