Hmbown/CodeWhale · error · ValueError
disabled_tools must use Codewhale catalog identifiers: ${",
Error message
disabled_tools must use Codewhale catalog identifiers: ${", ".join(repr(tool) for tool in invalid)} What it means
Every entry in config.disabled_tools must fullmatch ^[a-z0-9][a-z0-9_-]*$ — a lowercase Codewhale catalog identifier starting with an alphanumeric. The offending entries are interpolated into the message with repr(), so the error names exactly which strings failed. The list is joined into --disallowed-tools on the exec command line, so uppercase, spaces, dots, or a leading dash break the CLI contract.
Source
Thrown at integrations/verifiers-codewhale/codewhale_harness/harness.py:110
APPENDS_SYSTEM_PROMPT = True
SUPPORTS_MCP = True
SUPPORTS_USER_SIM = False
def _validate_config(self) -> None:
if not _VERSION.fullmatch(self.config.version):
raise ValueError("version must be a semantic release identifier")
if (
self.config.max_turns is not None
and not 1 <= self.config.max_turns <= 10_000
):
raise ValueError("max_turns must be between 1 and 10000")
invalid = [
tool
for tool in self.config.disabled_tools or []
if not _TOOL.fullmatch(tool)
]
if invalid:
raise ValueError(
"disabled_tools must use Codewhale catalog identifiers: "
+ ", ".join(repr(tool) for tool in invalid)
)
@property
def binary(self) -> str:
configured = (self.config.binary_path or "").strip()
return configured or DEFAULT_BINARY
async def setup(self, runtime: Runtime) -> None:
self._validate_config()
if self.config.binary_path:
logger.info("codewhale: verifying preinstalled %s", self.binary)
result = await runtime.run([self.binary, "--version"], {})
version_text = f"{result.stdout}\n{result.stderr}"
if result.exit_code != 0 or not _has_version(
version_text, self.config.version
):View on GitHub (pinned to 8880682c63)
Solutions
- Use exact catalog identifiers: lowercase letters, digits, underscores, hyphens
- List the tool catalog with the binary's tool-listing command and copy names verbatim
- Filter entries through the same regex before building the config
Example fix
# before config = CodewhaleHarnessConfig(version='0.9.1', disabled_tools=['Bash', 'Read File']) # after config = CodewhaleHarnessConfig(version='0.9.1', disabled_tools=['bash', 'read_file'])
Defensive patterns
Strategy: type-guard
Validate before calling
import re _TOOL = re.compile(r'^[a-z0-9][a-z0-9_-]*$') tools = [t for t in configured_tools if _TOOL.fullmatch(t)] config = CodewhaleHarnessConfig(version='0.9.1', disabled_tools=tools)
Type guard
import re
_TOOL = re.compile(r'^[a-z0-9][a-z0-9_-]*$')
def valid_tool_id(t):
return isinstance(t, str) and _TOOL.fullmatch(t) is not None Prevention
- Source tool names from the binary's catalog listing, not prose
- Lowercase and strip whitespace at ingestion
- Reject dotted or glob-form ids early in config loading
When it happens
Trigger: disabled_tools=['Bash'], ['read file'], ['-grep'], or dotted names like ['fs.read_file'].
Common situations: Copying display names from another agent CLI; mixing glob patterns or module-qualified ids into the list; case-changing config merges.
Related errors
- version must be a semantic release identifier
- max_turns must be between 1 and 10000
- ${name} is required
- ${names.join(" or ")} is required
- ${name} is required
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/4e2cb6269ef0330d.
Report an issue: GitHub.