Skip to content

Instantly share code, notes, and snippets.

@miguelrios
Last active January 23, 2026 02:42
Show Gist options
  • Select an option

  • Save miguelrios/1c15b907c39a3721a8dea838c572ba4b to your computer and use it in GitHub Desktop.

Select an option

Save miguelrios/1c15b907c39a3721a8dea838c572ba4b to your computer and use it in GitHub Desktop.
Claude CLI bug: --agents flag doesn't support @filepath syntax (affects SDK with large agent configs)
#!/usr/bin/env python3
"""
Minimal reproducible example: Claude Agent SDK @filepath bug for --agents flag
BUG SUMMARY:
When custom agents are passed to ClaudeAgentOptions and the resulting command line
exceeds 100K characters, the SDK writes agents to a temp file and passes `@filepath`
to the CLI. However, the CLI doesn't support this syntax for --agents, causing
custom agents to be silently ignored.
STEPS TO REPRODUCE:
1. pip install claude-agent-sdk
2. Set ANTHROPIC_API_KEY environment variable
3. Run: python sdk-agents-filepath-bug-v2.py
EXPECTED: Custom agents appear in the init message's agents list
ACTUAL: Custom agents are missing when prompt size triggers @filepath fallback
Environment:
- Claude CLI version: 2.1.12
- claude-agent-sdk version: 0.1.x
- OS: Linux (Ubuntu 24.04)
"""
import asyncio
import json
import subprocess
import tempfile
import os
# =============================================================================
# TEST 1: SDK with small agents config (direct JSON) - WORKS
# =============================================================================
async def test_sdk_small_agents():
"""Test SDK with small agent config - agents passed as direct JSON."""
print("=" * 70)
print("TEST 1: SDK with small agents config (<100K chars) - Expected: WORKS")
print("=" * 70)
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AgentDefinition
from claude_agent_sdk.types import SystemMessage, AssistantMessage
# Small agent config - will be passed as direct JSON
agents = {
"my-test-agent": AgentDefinition(
description="A custom test agent for bug reproduction",
prompt="You are a helpful test agent.",
)
}
options = ClaudeAgentOptions(
agents=agents,
max_turns=1,
permission_mode="bypassPermissions",
)
print(f"Agent config size estimate: ~100 chars (well under 100K limit)")
print("SDK will pass agents as direct JSON on command line")
found_agent = False
client = ClaudeSDKClient(options=options)
try:
await client.connect()
await client.query("Say 'hello' and nothing else.")
async for message in client.receive_messages():
# Check init message for agents list
if isinstance(message, SystemMessage):
data = getattr(message, 'data', {}) or {}
subtype = getattr(message, 'subtype', None)
if subtype == "init":
agents_list = data.get("agents", [])
print(f"\nInit message agents: {agents_list}")
found_agent = "my-test-agent" in agents_list
break
# Also check for assistant response to know we're done
if isinstance(message, AssistantMessage):
break
finally:
await client.disconnect()
if found_agent:
print("✅ PASS: 'my-test-agent' found in init message")
else:
print("❌ FAIL: 'my-test-agent' NOT found in init message")
return found_agent
# =============================================================================
# TEST 2: SDK with large agents config (@filepath fallback) - FAILS (BUG)
# =============================================================================
async def test_sdk_large_agents():
"""Test SDK with large agent config - triggers @filepath fallback."""
print("\n" + "=" * 70)
print("TEST 2: SDK with large agents config (>100K chars) - Expected: WORKS, Actual: FAILS")
print("=" * 70)
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AgentDefinition
from claude_agent_sdk.types import SystemMessage, AssistantMessage
from dataclasses import asdict
# Large agent config - will trigger @filepath fallback
# SDK's _CMD_LENGTH_LIMIT is 100,000 chars
large_prompt = "You are a helpful assistant. " * 2000 # ~60K chars per agent
agents = {
"my-large-agent-1": AgentDefinition(
description="First large test agent",
prompt=large_prompt,
),
"my-large-agent-2": AgentDefinition(
description="Second large test agent",
prompt=large_prompt,
),
}
# Estimate JSON size
agents_json = json.dumps({
name: {k: v for k, v in asdict(agent).items() if v is not None}
for name, agent in agents.items()
})
print(f"Agent config size: {len(agents_json):,} chars")
print(f"SDK _CMD_LENGTH_LIMIT: 100,000 chars")
print(f"Will trigger @filepath fallback: {len(agents_json) > 100000}")
print("\nWatch for SDK log: 'Command line length (X) exceeds limit (100000). Using temp file for --agents'")
options = ClaudeAgentOptions(
agents=agents,
max_turns=1,
permission_mode="bypassPermissions",
)
found_agent = False
client = ClaudeSDKClient(options=options)
try:
await client.connect()
await client.query("Say 'hello' and nothing else.")
async for message in client.receive_messages():
# Check init message for agents list
if isinstance(message, SystemMessage):
data = getattr(message, 'data', {}) or {}
subtype = getattr(message, 'subtype', None)
if subtype == "init":
agents_list = data.get("agents", [])
print(f"\nInit message agents: {agents_list}")
found_agent = "my-large-agent-1" in agents_list or "my-large-agent-2" in agents_list
break
if isinstance(message, AssistantMessage):
break
finally:
await client.disconnect()
if found_agent:
print("✅ PASS: Large agents found in init message")
else:
print("❌ FAIL: Large agents NOT found in init message (BUG!)")
print("\n🐛 The SDK wrote agents to a temp file and passed @filepath to CLI,")
print(" but the CLI doesn't support @filepath syntax for --agents flag.")
return found_agent
# =============================================================================
# SUPPLEMENTARY: Direct CLI test showing @filepath doesn't work
# =============================================================================
def test_cli_filepath_direct():
"""Supplementary test: CLI @filepath syntax directly."""
print("\n" + "=" * 70)
print("SUPPLEMENTARY: Direct CLI test - @filepath vs direct JSON")
print("=" * 70)
agents_dict = {
"cli-test-agent": {
"description": "CLI test agent",
"prompt": "You are a test agent."
}
}
# Test 1: Direct JSON (works)
print("\n--- Direct JSON ---")
agents_json = json.dumps(agents_dict)
cmd1 = ["claude", "--agents", agents_json, "--print", "--", "List agent names only"]
result1 = subprocess.run(cmd1, capture_output=True, text=True, timeout=60)
direct_works = "cli-test-agent" in result1.stdout.lower()
print(f"Command: claude --agents '<json>' --print -- '...'")
print(f"Result: {'✅ Agent found' if direct_works else '❌ Agent NOT found'}")
# Test 2: @filepath (fails)
print("\n--- @filepath syntax ---")
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(agents_dict, f)
temp_path = f.name
cmd2 = ["claude", "--agents", f"@{temp_path}", "--print", "--", "List agent names only"]
result2 = subprocess.run(cmd2, capture_output=True, text=True, timeout=60)
filepath_works = "cli-test-agent" in result2.stdout.lower()
print(f"Command: claude --agents '@{temp_path}' --print -- '...'")
print(f"Result: {'✅ Agent found' if filepath_works else '❌ Agent NOT found (BUG)'}")
os.unlink(temp_path)
return direct_works, filepath_works
# =============================================================================
# MAIN
# =============================================================================
async def main():
print("Claude Agent SDK @filepath Bug - Minimal Reproducible Example")
print("=" * 70)
# Check versions
result = subprocess.run(["claude", "--version"], capture_output=True, text=True)
print(f"Claude CLI version: {result.stdout.strip()}")
try:
import claude_agent_sdk
print(f"claude-agent-sdk: installed")
except ImportError:
print("ERROR: claude-agent-sdk not installed. Run: pip install claude-agent-sdk")
return 1
print()
# Run SDK tests
test1_passed = await test_sdk_small_agents()
test2_passed = await test_sdk_large_agents()
# Run supplementary CLI test
cli_direct, cli_filepath = test_cli_filepath_direct()
# Summary
print("\n" + "=" * 70)
print("SUMMARY")
print("=" * 70)
print(f"SDK Test 1 (small config, direct JSON): {'✅ PASS' if test1_passed else '❌ FAIL'}")
print(f"SDK Test 2 (large config, @filepath): {'✅ PASS' if test2_passed else '❌ FAIL (BUG)'}")
print(f"CLI Test (direct JSON): {'✅ PASS' if cli_direct else '❌ FAIL'}")
print(f"CLI Test (@filepath): {'✅ PASS' if cli_filepath else '❌ FAIL (BUG)'}")
if test1_passed and not test2_passed:
print("\n" + "=" * 70)
print("🐛 BUG CONFIRMED")
print("=" * 70)
print("""
The Claude CLI does not support @filepath syntax for the --agents flag.
When the SDK's command line exceeds 100K characters (due to large agent prompts),
it writes agents JSON to a temp file and passes `@filepath` to the CLI:
# From claude_agent_sdk/_internal/transport/subprocess_cli.py (lines 336-357)
if len(cmd_str) > _CMD_LENGTH_LIMIT and self._options.agents:
temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
temp_file.write(agents_json_value)
temp_file.close()
cmd[agents_idx + 1] = f"@{temp_file.name}" # CLI doesn't understand this!
The CLI interprets `@/tmp/xxx.json` as a literal string instead of reading from the file,
causing custom agents to be silently ignored.
IMPACT:
- Users with large agent configurations (detailed prompts, multiple agents) are affected
- Custom agents silently fail to register - no error is raised
- Difficult to debug because everything appears to work except agents are missing
WORKAROUND:
import claude_agent_sdk._internal.transport.subprocess_cli as cli
cli._CMD_LENGTH_LIMIT = 500000 # Increase from 100K to 500K
SUGGESTED FIX:
Either:
1. Add @filepath support to CLI for --agents flag (consistent with other flags)
2. Use stdin or environment variable for large agent configs in SDK
3. Document the 100K limit and recommend shorter prompts
""")
return 1
return 0
if __name__ == "__main__":
exit(asyncio.run(main()))
#!/usr/bin/env python3
"""
Minimal reproducible example: Claude CLI doesn't support @filepath for --agents flag
BUG SUMMARY:
The Claude Agent SDK writes agents JSON to a temp file when command line exceeds 100K chars,
passing `@filepath` to the CLI. However, the CLI interprets `@filepath` as a literal string
instead of reading from the file, causing custom agents to be silently ignored.
STEPS TO REPRODUCE:
1. Run this script: python sdk-agents-filepath-bug.py
2. Observe that with direct JSON, custom agent appears in available agents list
3. Observe that with @filepath syntax, custom agent is MISSING
EXPECTED: Both methods should register the custom agent
ACTUAL: @filepath method silently fails to register the agent
Environment:
- Claude CLI version: 2.1.12
- claude-agent-sdk version: 0.1.x
- OS: Linux (Ubuntu 24.04)
"""
import subprocess
import json
import tempfile
import sys
def test_agents_direct_json():
"""Test 1: Agents passed as direct JSON string - WORKS"""
print("=" * 70)
print("TEST 1: Direct JSON (small payload) - Expected: WORKS")
print("=" * 70)
agents_json = json.dumps({
"my-custom-agent": {
"description": "A custom test agent",
"prompt": "You are a helpful test agent."
}
})
cmd = [
"claude",
"--agents", agents_json,
"--print",
"--", "List all available agents. Just list agent names, nothing else."
]
print(f"Command: claude --agents '<json>' --print -- 'List agents'")
print(f"Agents JSON length: {len(agents_json)} chars")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
output = result.stdout
if "my-custom-agent" in output.lower():
print("✅ PASS: 'my-custom-agent' found in output")
else:
print("❌ FAIL: 'my-custom-agent' NOT found in output")
print(f"\nOutput preview:\n{output[:500]}...")
return "my-custom-agent" in output.lower()
def test_agents_filepath():
"""Test 2: Agents passed via @filepath - FAILS (BUG)"""
print("\n" + "=" * 70)
print("TEST 2: @filepath syntax - Expected: WORKS, Actual: FAILS (BUG)")
print("=" * 70)
agents_dict = {
"my-custom-agent": {
"description": "A custom test agent",
"prompt": "You are a helpful test agent."
}
}
# Write to temp file (same as SDK does)
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(agents_dict, f)
temp_path = f.name
print(f"Temp file: {temp_path}")
print(f"File contents: {json.dumps(agents_dict)}")
# Use @filepath syntax (same as SDK does when command line > 100K chars)
cmd = [
"claude",
"--agents", f"@{temp_path}",
"--print",
"--", "List all available agents. Just list agent names, nothing else."
]
print(f"Command: claude --agents '@{temp_path}' --print -- 'List agents'")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
output = result.stdout
if "my-custom-agent" in output.lower():
print("✅ PASS: 'my-custom-agent' found in output")
else:
print("❌ FAIL: 'my-custom-agent' NOT found in output (BUG!)")
print(f"\nOutput preview:\n{output[:500]}...")
# Cleanup
import os
os.unlink(temp_path)
return "my-custom-agent" in output.lower()
def test_sdk_triggers_filepath():
"""Test 3: Show that SDK uses @filepath when JSON > 100K chars"""
print("\n" + "=" * 70)
print("TEST 3: SDK behavior with large agents config")
print("=" * 70)
# Create agent config that exceeds 100K chars (SDK's _CMD_LENGTH_LIMIT)
large_prompt = "A" * 50000 # 50K chars per agent
agents_dict = {
f"agent-{i}": {
"description": f"Test agent {i}",
"prompt": large_prompt
}
for i in range(3) # 3 agents * ~50K = ~150K chars
}
agents_json = json.dumps(agents_dict)
print(f"Agents JSON size: {len(agents_json):,} chars")
print(f"SDK _CMD_LENGTH_LIMIT: 100,000 chars")
print(f"Would trigger @filepath fallback: {len(agents_json) > 100000}")
print("\nSDK code that triggers this (subprocess_cli.py lines 336-357):")
print("""
if len(cmd_str) > _CMD_LENGTH_LIMIT and self._options.agents:
# Command is too long - use temp file for agents
temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
temp_file.write(agents_json_value)
temp_file.close()
cmd[agents_idx + 1] = f"@{temp_file.name}" # <-- CLI doesn't understand this!
""")
def main():
print("Claude CLI @filepath Bug - Minimal Reproducible Example")
print("=" * 70)
print()
# Check Claude CLI version
result = subprocess.run(["claude", "--version"], capture_output=True, text=True)
print(f"Claude CLI version: {result.stdout.strip()}")
print()
# Run tests
test1_passed = test_agents_direct_json()
test2_passed = test_agents_filepath()
test_sdk_triggers_filepath()
# Summary
print("\n" + "=" * 70)
print("SUMMARY")
print("=" * 70)
print(f"Test 1 (direct JSON): {'✅ PASS' if test1_passed else '❌ FAIL'}")
print(f"Test 2 (@filepath): {'✅ PASS' if test2_passed else '❌ FAIL (BUG)'}")
if test1_passed and not test2_passed:
print("\n🐛 BUG CONFIRMED: CLI accepts direct JSON but ignores @filepath syntax")
print("\nIMPACT: When using Claude Agent SDK with large agent configs (>100K chars),")
print("custom agents are silently ignored because the SDK falls back to @filepath.")
print("\nWORKAROUND: Monkey-patch SDK to increase _CMD_LENGTH_LIMIT:")
print("""
import claude_agent_sdk._internal.transport.subprocess_cli as cli
cli._CMD_LENGTH_LIMIT = 500000 # Increase from 100K to 500K
""")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment