shareAI-lab/learn-claude-code · error · GoalError

block_cap must be at least 1

Error message

block_cap must be at least 1

What it means

GoalController was constructed with block_cap < 1. block_cap limits how many times the Stop hook may consecutively block the agent from stopping (DEFAULT_STOP_HOOK_BLOCK_CAP is 8); a cap of 0 or negative would make the loop logic meaningless, so the constructor fails fast (s17_goal_loop/code.py:271).

Source

Thrown at s17_goal_loop/code.py:271

            ),
            messages=[{"role": "user", "content": prompt}],
            max_tokens=self.max_tokens,
        )
        value = _parse_json_object(_extract_text(response.content))
        return GoalEvaluation(**value)


class GoalController:
    """Session-scoped goal state plus the Stop hook decision."""

    def __init__(
        self,
        evaluator: Any,
        block_cap: int = DEFAULT_STOP_HOOK_BLOCK_CAP,
        events: list[dict[str, Any]] | None = None,
    ):
        if block_cap < 1:
            raise GoalError("block_cap must be at least 1")
        self.evaluator = evaluator
        self.block_cap = block_cap
        self.events = events if events is not None else []
        self.active: GoalState | None = None
        self.last_status: dict[str, Any] | None = None
        self.consecutive_blocks = 0

    def begin_query(self) -> None:
        self.consecutive_blocks = 0

    def set_goal(self, condition: str, tokens_at_start: int = 0) -> GoalState:
        condition = condition.strip()
        if not condition:
            raise GoalError("goal condition cannot be empty")
        if len(condition) > MAX_GOAL_LENGTH:
            raise GoalError(
                f"goal condition cannot exceed {MAX_GOAL_LENGTH} characters"
            )

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Pass block_cap >= 1 (1 means the hook blocks at most once, then lets the agent stop)
  2. If disabling blocking was the intent, do not construct the controller with a hook, or set the cap to 1
  3. Set CLAUDE_CODE_STOP_HOOK_BLOCK_CAP to a positive integer such as 8, or unset it to accept the default

Example fix

# before
controller = GoalController(evaluator, block_cap=0)

# after
controller = GoalController(evaluator, block_cap=1)
Defensive patterns

Strategy: validation

Validate before calling

raw_cap = int(os.getenv("CLAUDE_CODE_STOP_HOOK_BLOCK_CAP", 8))
if raw_cap < 1:
    raise SystemExit("CLAUDE_CODE_STOP_HOOK_BLOCK_CAP must be >= 1")
controller = GoalController(evaluator, block_cap=raw_cap)

Type guard

def is_valid_block_cap(value: object) -> bool:
    return isinstance(value, int) and not isinstance(value, bool) and value >= 1

Try / catch

try:
    GoalController(evaluator, block_cap=cap)
except GoalError:
    cap = DEFAULT_STOP_HOOK_BLOCK_CAP
    controller = GoalController(evaluator, block_cap=cap)

Prevention

When it happens

Trigger: Directly instantiating GoalController(evaluator, block_cap=0) or block_cap=-3. Indirectly: setting the environment variable CLAUDE_CODE_STOP_HOOK_BLOCK_CAP to 0 or a negative number, which make_live_session converts with int() and passes in.

Common situations: Trying to 'disable' the stop-hook blocking by setting the cap to 0 (use a cap of 1 for single-block behavior instead); misreading the env var as a boolean flag; passing a value parsed from user config without validation.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/4bfcacbc50f28c06. Report an issue: GitHub.