-
Notifications
You must be signed in to change notification settings - Fork 4
Troubleshooting and FAQ
Solutions for common issues, debugging strategies, and answers to frequently asked questions about the Claude Agent SDK for Laravel.
Cause: The SDK cannot locate the claude binary. The PHP process has a different $PATH than your terminal session.
Diagnosis:
# Verify the CLI is installed
which claude
claude --version
# Check what PHP sees
php -r "echo exec('which claude');"Solutions:
-
Install globally:
npm install -g @anthropic-ai/claude-code -
Set an explicit path in
config/claude-agent.php:'cli_path' => '/usr/local/bin/claude',
-
Symlink to a standard location that PHP can access:
sudo ln -s $(which claude) /usr/local/bin/claude
Note: When PHP runs as
www-data(Apache/Nginx), the user's npm global bin directory is typically not on its$PATH. An explicitcli_pathis the most reliable fix.
Cause: The PHP process user does not have execute permission on the claude binary, or does not have read/write access to the working directory.
Solutions:
# Make the CLI executable
chmod +x $(which claude)
# Verify the PHP user can access the working directory
sudo -u www-data ls /path/to/your/project
# Check file ownership
ls -la $(which claude)If running in a container, ensure the binary is installed during the Docker build and is accessible to the runtime user.
Cause: Version constraints in your composer.json conflict with the SDK's requirements (PHP 8.1+, certain Symfony Process versions).
Solutions:
# Check what is conflicting
composer why-not anthropic/claude-agent-sdk
# Update with dependency resolution
composer require anthropic/claude-agent-sdk --with-all-dependencies
# If needed, update the conflicting package first
composer update symfony/process --with-all-dependenciesPossible causes:
-
No
process_timeoutset -- the process runs until the CLI finishes or the PHP max execution time kills it. -
CLI waiting for interactive input -- permission mode is
defaultand the CLI prompts for tool confirmation. - Network issue -- the CLI cannot reach the Anthropic API.
Solutions:
// Always set a timeout in production
'process_timeout' => 120, // Kill after 2 minutes
// Use non-interactive permission mode
'permission_mode' => 'dontAsk',
'allowed_tools' => ['Read', 'Grep', 'Glob'],Tip: In queue workers, set
process_timeoutlower than your queue connection'sretry_afterto prevent overlapping retries.
This is a ProcessException. Common causes:
-
Authentication failure:
ANTHROPIC_API_KEYis missing or invalid - Invalid CLI flags: an option the installed CLI version does not support
- Model not available: the requested model is not accessible with your API key
Debug by inspecting stderr:
use ClaudeAgentSDK\Exceptions\ProcessException;
try {
$result = ClaudeAgent::query('...');
} catch (ProcessException $e) {
dd($e->exitCode, $e->stderr);
}If $result->text() returns null or an empty string:
-
Check for errors first:
$result->isError()may betrue - Max turns reached: the agent ran out of turns before producing a final answer
- Budget exceeded: the spend ceiling stopped the conversation early
-
No ResultMessage: if the messages array has no
ResultMessage,text()returnsnull
$result = ClaudeAgent::query('...');
if ($result->isError()) {
Log::warning('Agent error', ['text' => $result->text(), 'turns' => $result->turns()]);
}
// Inspect all messages for debugging
foreach ($result->messages as $msg) {
dump(get_class($msg), $msg);
}Cause: The CLI outputs a line that looks like JSON (starts with { or [) but is malformed. This typically happens with:
- A CLI version mismatch (SDK expects a different output format)
- Corrupt or truncated output from a killed process
- Interleaved debug output from verbose mode
Solutions:
# Update the CLI to the latest version
npm update -g @anthropic-ai/claude-code
# Verify CLI output format
claude --print "Hello" --output-format stream-json --verbose 2>/dev/nullCause: The agent reads a very large codebase or produces extensive output, causing PHP memory exhaustion.
Solutions:
-
Use streaming:
stream()uses constant memory via generators;query()holds everything in memory -
Reduce tool scope: limit
allowedToolsand setcwdto a subdirectory - Increase PHP memory limit if the response itself is legitimately large
-
Set
max_turnsto prevent unbounded conversation growth
// Stream instead of query for large tasks
foreach (ClaudeAgent::stream('Analyze all files', $options) as $message) {
// Process each message individually -- previous messages can be GC'd
}Possible causes:
-
Extended thinking tokens: if
max_thinking_tokensis set, thinking tokens add to cost - Cache misses: first queries are more expensive; subsequent queries benefit from prompt caching
- Multiple turns: the agent may take many tool-use turns, each incurring API calls
- Large context: reading many files pushes input token count up
Diagnosis:
$result = ClaudeAgent::query('...');
echo "Cost: $" . $result->costUsd() . "\n";
echo "Turns: " . $result->turns() . "\n";
echo "Cache read: " . $result->cacheReadTokens() . "\n";
echo "Cache created: " . $result->cacheCreationTokens() . "\n";
foreach ($result->modelUsage() as $model => $usage) {
echo "{$model}: input={$usage->inputTokens}, output={$usage->outputTokens}, "
. "cacheHit={$usage->cacheHitRate()}\n";
}When max_budget_usd is exceeded, the agent stops and returns a result-level error (not an exception):
$result = ClaudeAgent::query('...', ClaudeAgentOptions::make()->maxBudgetUsd(0.10));
if ($result->isError()) {
// Likely budget exceeded or max turns
echo $result->text(); // "Budget exceeded"
}Solutions: Increase max_budget_usd, reduce max_turns, limit tools, or use a cheaper model.
Possible causes:
- Session expired: old sessions may be cleaned up by the CLI
- Different user: sessions are tied to the OS user that created them
-
Different machine: session data is stored locally in
~/.claude/sessions/ - Invalid session ID: the stored ID is corrupted or from a different environment
Solution: Treat sessions as ephemeral. Store session IDs with a TTL (2-4 hours) and handle resume failures gracefully:
use ClaudeAgentSDK\Facades\ClaudeAgent;
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
use ClaudeAgentSDK\Exceptions\ProcessException;
$sessionId = cache()->get("agent_session:{$userId}");
try {
$options = $sessionId
? ClaudeAgentOptions::make()->resume($sessionId)
: ClaudeAgentOptions::make();
$result = ClaudeAgent::query('Continue the analysis', $options);
} catch (ProcessException $e) {
// Session may have expired -- start fresh
$result = ClaudeAgent::query('Start a new analysis');
}
cache()->put("agent_session:{$userId}", $result->sessionId, now()->addHours(2));continueConversation() uses the CLI's --continue flag, which resumes the last session for the current CLI user on the current machine. In a web server context, this means the www-data user's last session -- which is almost certainly not what you want.
Solution: Always use resume($sessionId) with an explicit session ID in multi-user or server environments. Reserve continueConversation() for local development and single-user CLI scripts.
When using cloud providers, authentication is handled by the CLI through environment variables:
AWS Bedrock:
CLAUDE_CODE_USE_BEDROCK=true
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1Google Vertex AI:
CLAUDE_CODE_USE_VERTEX=true
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
CLOUD_ML_REGION=us-central1Custom API endpoint:
ANTHROPIC_BASE_URL=https://your-proxy.example.com/v1
ANTHROPIC_AUTH_TOKEN=your-bearer-tokenNote: Enable only one provider at a time. If auth fails, verify credentials work with the CLI directly:
claude --print "Hello".
Does the SDK support PHP 8.0? No. PHP 8.1 or higher is required due to the use of enums, readonly properties, fibers, and intersection types.
Can I use the SDK without Laravel?
Yes. Instantiate ClaudeAgentManager directly without the service provider:
use ClaudeAgentSDK\ClaudeAgentManager;
use ClaudeAgentSDK\Options\ClaudeAgentOptions;
$agent = new ClaudeAgentManager([
'model' => 'claude-sonnet-4-5-20250929',
'permission_mode' => 'dontAsk',
'allowed_tools' => ['Read', 'Grep'],
]);
$result = $agent->query('Analyze this code');How much does each query cost?
Cost varies by model, input size, output length, thinking tokens, and cache hit rate. Use $result->costUsd() to track actual costs. A simple question typically costs $0.001-$0.01; complex multi-turn tasks can cost $0.10-$1.00+.
Can the agent access the internet?
Yes, if you include WebFetch and/or WebSearch in the allowed tools. By default, no web tools are enabled.
Is work persisted across HTTP requests?
Not automatically. Use Session Management to store the sessionId and resume conversations across requests.
Can I run multiple queries concurrently?
Yes. Each query() or stream() call spawns a separate CLI process. You can run them in parallel using Laravel jobs, async processes, or concurrent HTTP requests. Each process is independent.
How do I limit which files the agent can access?
Set cwd to a specific directory and restrict tools. The agent operates within the working directory. Use addDir() to grant access to additional directories without changing cwd. See Options Reference for details.
Can I use this in serverless environments (AWS Lambda)?
Yes, provided the Claude Code CLI is installed in the Lambda runtime (e.g. via a Lambda layer with Node.js). Be aware of cold start times -- the CLI binary needs to initialize on each cold start. Set a reasonable process_timeout to account for this.
What happens if the CLI crashes mid-query?
A ProcessException is thrown with the exit code and stderr. If you are using stream(), you may have received partial AssistantMessage objects before the crash -- these are still usable but the conversation did not complete successfully.
How do I update the SDK?
composer update anthropic/claude-agent-sdkCheck the changelog for breaking changes before upgrading major versions.
Can I use a custom or fine-tuned model?
Yes, pass any model identifier the CLI supports via ClaudeAgentOptions::make()->model('your-model-id') or set it in config. The SDK passes the model string directly to the CLI.
How do I see what CLI command the SDK is running? Enable verbose logging or inspect the options object:
$options = ClaudeAgentOptions::make()->model('claude-sonnet-4-5-20250929')->tools(['Read']);
dd($options->toCliArgs());- GitHub Issues: Report bugs and request features at the claude-agent-sdk-laravel repository
-
Contributing: See
CONTRIBUTING.mdin the repository root for development setup and pull request guidelines
- Error Handling -- exception hierarchy and comprehensive error patterns
- Testing Your Integration -- mock the SDK for fast, free tests
- Configuration -- full config reference and environment-specific setup
- Home -- back to the documentation index