> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/charlietyn/openapi-generator/llms.txt
> Use this file to discover all available pages before exploring further.

# Edge Cases & Troubleshooting

> Handle config caching, concurrent generation, timeouts, multi-tenant deployments, and common issues

This guide covers edge cases, extreme scenarios, and troubleshooting strategies for production environments.

## Config Caching with Runtime `env()` Usage

### Symptom

Changing `.env` values (e.g., `APP_NAME`, `APP_URL`) does not affect generated documentation output.

### Cause

Laravel's `config:cache` freezes configuration values. Some package code reads `env()` directly at runtime instead of relying solely on cached config values. When config is cached, these `env()` calls return `null`.

### Affected Areas

* `PlaceholderHelper::getProjectName()` - Reads `APP_NAME` via `env()`
* `EnvironmentGenerator::buildBaseEnvironmentData()` - Reads `APP_URL` via `env()`

### Mitigation

<Steps>
  <Step title="Prefer config overrides">
    Instead of relying on `.env` values, set config values explicitly in `config/openapi.php`:

    ```php theme={null}
    'info' => [
        'title' => 'My API Documentation',  // Instead of env('APP_NAME')
        'version' => '1.0.0',
    ],

    'environments' => [
        'base' => [
            'app_url' => 'https://api.example.com',  // Instead of env('APP_URL')
        ],
    ],
    ```
  </Step>

  <Step title="Clear config cache after changes">
    When changing `.env` values:

    ```bash theme={null}
    php artisan config:clear
    php artisan openapi:generate
    ```
  </Step>

  <Step title="Avoid config cache in development">
    Only use `config:cache` in production. In local/staging, keep config uncached to allow dynamic `.env` changes.
  </Step>
</Steps>

### How to Reproduce

```bash theme={null}
# Cache config
php artisan config:cache

# Change APP_NAME in .env
sed -i 's/APP_NAME=.*/APP_NAME=NewName/' .env

# Generate docs
php artisan openapi:generate

# Observe that title in openapi.json still shows old name
cat storage/app/public/openapi/openapi.json | grep '"title"'
```

### How to Test

```bash theme={null}
# Clear cache
php artisan config:clear

# Regenerate
php artisan openapi:generate

# Verify new values appear
cat storage/app/public/openapi/openapi.json | grep '"title"'
```

<Warning>
  **Production Impact**

  If you deploy with cached config and rely on `env()` values, your documentation will show stale or incorrect information. Always set critical values in config files, not `.env`.
</Warning>

***

## Concurrent Generation Writes to Same Output File

### Symptom

Partially written or inconsistent JSON/YAML files in `storage/app/public/openapi`. Files may contain truncated JSON or mixed content from multiple generations.

### Cause

Multiple workers, developers, or CI jobs running `openapi:generate` simultaneously write to the same output filenames, causing race conditions.

### Mitigation

<Tabs>
  <Tab title="Unique Output Paths">
    Use `--output` to write to unique file names per job:

    ```bash theme={null}
    # Include timestamp or job ID
    php artisan openapi:generate --output=/tmp/openapi-$(date +%s).json

    # In CI: use build number
    php artisan openapi:generate --output=/tmp/openapi-build-${CI_BUILD_ID}.json
    ```
  </Tab>

  <Tab title="File Locking">
    Implement file locking in your application:

    ```php theme={null}
    use Illuminate\Support\Facades\Cache;

    if (Cache::lock('openapi-generation', 60)->get()) {
        Artisan::call('openapi:generate', ['--all' => true]);
        Cache::lock('openapi-generation')->release();
    }
    ```
  </Tab>

  <Tab title="Single Queue Worker">
    Run generation in a dedicated queue with a single worker:

    ```php theme={null}
    // Job
    class GenerateOpenApiJob implements ShouldQueue
    {
        public $queue = 'docs-generation';
        
        public function handle()
        {
            Artisan::call('openapi:generate', ['--all' => true]);
        }
    }
    ```

    ```bash theme={null}
    # Run single worker for docs queue
    php artisan queue:work --queue=docs-generation --once
    ```
  </Tab>
</Tabs>

### How to Reproduce

```bash theme={null}
# Terminal 1
php artisan openapi:generate --all &

# Terminal 2 (immediately after)
php artisan openapi:generate --all &

# Wait for both to complete, then inspect output
cat storage/app/public/openapi/openapi.json | jq .
# May show JSON parse errors
```

### How to Test

Add file locking and run the same concurrent test. Verify that one generation waits for the other to complete.

***

## Long-Running Queue Workers and Stale Caches

### Symptom

Old or stale documentation when running generation from a long-lived queue worker. New routes or validation rules don't appear in output.

### Cause

The generator caches output when `openapi.cache.enabled` is `true`. Long-lived workers reuse cached data even after code changes.

### Mitigation

<Steps>
  <Step title="Disable cache for queue jobs">
    ```php theme={null}
    class GenerateOpenApiJob implements ShouldQueue
    {
        public function handle()
        {
            Artisan::call('openapi:generate', [
                '--all' => true,
                '--no-cache' => true,  // Force fresh generation
            ]);
        }
    }
    ```
  </Step>

  <Step title="Reduce cache TTL">
    For frequently changing APIs, reduce cache time-to-live:

    ```php theme={null}
    'cache' => [
        'enabled' => true,
        'ttl' => 300,  // 5 minutes instead of 1 hour
    ],
    ```
  </Step>

  <Step title="Clear cache on deployment">
    Add cache clearing to deployment scripts:

    ```bash theme={null}
    php artisan cache:clear
    php artisan config:clear
    php artisan openapi:generate --all
    ```
  </Step>
</Steps>

### How to Reproduce

```bash theme={null}
# Enable cache with long TTL
php artisan config:set openapi.cache.enabled true
php artisan config:set openapi.cache.ttl 3600

# Generate once
php artisan openapi:generate

# Add a new route
echo "Route::get('new-endpoint', fn() => 'test');" >> routes/api.php

# Regenerate (uses cache)
php artisan openapi:generate

# Verify new route is missing from openapi.json
```

### How to Test

```bash theme={null}
# Regenerate with --no-cache
php artisan openapi:generate --no-cache

# Verify new route appears
cat storage/app/public/openapi/openapi.json | grep 'new-endpoint'
```

***

## Large Route Sets Causing Timeouts

### Symptom

HTTP requests to `/documentation/openapi.json` time out or return 500 errors. Large applications with hundreds of routes cause generation to exceed PHP execution limits.

### Cause

On-demand HTTP generation performs heavy introspection:

* Route extraction
* FormRequest validation analysis
* Model schema reflection
* Template processing

For large apps, this exceeds the default 30-60 second timeout.

### Mitigation

<Tabs>
  <Tab title="Generate via CLI">
    **Recommended**: Generate docs via CLI and serve static files:

    ```bash theme={null}
    # Generate during deployment
    php artisan openapi:generate --all

    # Symlink to public directory
    ln -s storage/app/public/openapi public/docs
    ```

    Serve from:

    ```
    https://api.example.com/docs/openapi.json
    https://api.example.com/docs/postman-all.json
    ```
  </Tab>

  <Tab title="Increase Timeout">
    Increase PHP execution time for documentation endpoints:

    ```php theme={null}
    // In RouteServiceProvider or middleware
    Route::prefix('documentation')->group(function () {
        ini_set('max_execution_time', 300);  // 5 minutes
        
        Route::get('openapi.{format}', [OpenApiController::class, 'show']);
    });
    ```
  </Tab>

  <Tab title="Filter Routes">
    Reduce route count using filters:

    ```bash theme={null}
    # API type filtering
    curl "http://localhost:8000/documentation/openapi.json?api_type=mobile"
    ```

    Or exclude internal routes:

    ```php theme={null}
    'exclude_routes' => [
        'horizon.*',
        'telescope.*',
        'debugbar.*',
        'internal.*',
    ],
    ```
  </Tab>
</Tabs>

### How to Reproduce

Create a large number of routes:

```php theme={null}
// routes/api.php
for ($i = 0; $i < 500; $i++) {
    Route::get("test-{$i}", fn() => "test");
}
```

```bash theme={null}
# Try HTTP generation
curl http://localhost:8000/documentation/openapi.json
# May timeout or return 504 Gateway Timeout
```

### How to Test

Compare response times:

```bash theme={null}
# With filtering
time curl "http://localhost:8000/documentation/openapi.json?api_type=mobile"

# Without filtering
time curl "http://localhost:8000/documentation/openapi.json"
```

<Tip>
  **Performance Benchmarks**

  * **50 routes**: Less than 1 second
  * **200 routes**: 2-5 seconds
  * **500+ routes**: 10-30 seconds (consider CLI generation)
</Tip>

***

## Multi-Tenant Deployments

### Symptom

OpenAPI info title, server URLs, and environments do not reflect tenant-specific settings. All tenants get the same generic documentation.

### Cause

Configuration is global. The generator reads app-level config values, not tenant-specific overrides.

### Mitigation

<Steps>
  <Step title="Generate docs per tenant">
    ```php theme={null}
    foreach (Tenant::all() as $tenant) {
        $tenant->run(function () use ($tenant) {
            // Override config for this tenant
            config([
                'openapi.info.title' => "{$tenant->name} API",
                'openapi.servers' => [
                    ['url' => "https://{$tenant->domain}/api"],
                ],
                'openapi.output_path' => storage_path("app/tenants/{$tenant->id}/openapi"),
            ]);
            
            Artisan::call('openapi:generate', ['--all' => true]);
        });
    }
    ```
  </Step>

  <Step title="Store per-tenant output">
    Use tenant-specific output paths:

    ```php theme={null}
    'output_path' => storage_path("app/tenants/{$tenantId}/openapi"),
    ```

    Serve from tenant-specific URLs:

    ```
    https://tenant1.example.com/docs/openapi.json
    https://tenant2.example.com/docs/openapi.json
    ```
  </Step>

  <Step title="Dynamic HTTP routes">
    Serve tenant-specific docs via HTTP:

    ```php theme={null}
    Route::get('documentation/openapi.{format}', function ($format) {
        $tenant = tenant();
        
        $service = app(OpenApiServices::class);
        
        // Override config for current tenant
        config([
            'openapi.info.title' => "{$tenant->name} API",
            'openapi.servers' => [
                ['url' => "https://{$tenant->domain}/api"],
            ],
        ]);
        
        $spec = $service->generate(
            useCache: true,
            apiTypes: null,
            environment: 'production',
            generationType: 'openapi'
        );
        
        return response()->json($spec);
    });
    ```
  </Step>
</Steps>

### How to Reproduce

```php theme={null}
// Generate for tenant 1
config(['openapi.info.title' => 'Tenant 1 API']);
Artisan::call('openapi:generate');

// Generate for tenant 2 (without override)
Artisan::call('openapi:generate');

// Observe tenant 2 docs show "Tenant 1 API" title
```

### How to Test

```php theme={null}
// Generate with per-tenant overrides
foreach (['Tenant 1', 'Tenant 2'] as $tenantName) {
    config(['openapi.info.title' => "{$tenantName} API"]);
    Artisan::call('openapi:generate', [
        '--output' => storage_path("app/{$tenantName}/openapi.json"),
    ]);
}

// Verify each file has correct title
```

***

## Rate Limiting or Auth Middleware Blocking HTTP Docs

### Symptom

401/403/429 responses when accessing `/documentation/openapi.json`.

### Cause

The `openapi.routes.middleware` stack includes authentication, authorization, or rate limiting middleware.

### Mitigation

<Tabs>
  <Tab title="Remove Middleware">
    For public documentation:

    ```php theme={null}
    'routes' => [
        'enabled' => true,
        'prefix' => 'documentation',
        'middleware' => [],  // No auth or throttling
    ],
    ```
  </Tab>

  <Tab title="Custom Middleware Stack">
    Create a dedicated middleware group:

    ```php theme={null}
    // app/Http/Kernel.php
    protected $middlewareGroups = [
        'docs' => [
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            // No auth or throttling
        ],
    ];
    ```

    ```php theme={null}
    // config/openapi.php
    'routes' => [
        'middleware' => ['docs'],
    ],
    ```
  </Tab>

  <Tab title="Static File Serving">
    Serve pre-generated files instead of HTTP generation:

    ```bash theme={null}
    php artisan openapi:generate --all
    ln -s storage/app/public/openapi public/docs
    ```

    Configure your web server to serve `/docs/*` directly without Laravel middleware.
  </Tab>
</Tabs>

### How to Reproduce

```php theme={null}
// config/openapi.php
'routes' => [
    'middleware' => ['auth:sanctum', 'throttle:60,1'],
],
```

```bash theme={null}
# Without authentication
curl http://localhost:8000/documentation/openapi.json
# {"message":"Unauthenticated."}
```

### How to Test

Remove middleware and verify access:

```php theme={null}
'routes' => [
    'middleware' => [],
],
```

```bash theme={null}
curl http://localhost:8000/documentation/openapi.json
# Returns OpenAPI spec
```

***

## Invalid API Type Filters

### Symptom

422 validation error when requesting HTTP documentation with invalid API type.

```json theme={null}
{
  "message": "The given data was invalid.",
  "errors": {
    "api_type": ["Invalid API types: unknown"]
  }
}
```

### Cause

Requested API type is missing, disabled, or misspelled in `openapi.api_types`.

### Mitigation

<Steps>
  <Step title="Verify available types">
    ```bash theme={null}
    php artisan tinker
    >>> config('openapi.api_types')
    ```

    Check enabled API types:

    ```php theme={null}
    [
        'admin' => ['enabled' => true, ...],
        'mobile' => ['enabled' => true, ...],
        'site' => ['enabled' => false, ...],  // Disabled
    ]
    ```
  </Step>

  <Step title="Enable the API type">
    In `config/openapi.php`:

    ```php theme={null}
    'api_types' => [
        'site' => [
            'prefix' => 'api',
            'folder_name' => 'API Public',
            'enabled' => true,  // Changed from false
        ],
    ],
    ```
  </Step>

  <Step title="Add missing API type">
    If the type doesn't exist:

    ```php theme={null}
    'api_types' => [
        'partner' => [
            'prefix' => 'partner',
            'folder_name' => 'Partner API',
            'enabled' => true,
        ],
    ],
    ```
  </Step>
</Steps>

### How to Reproduce

```bash theme={null}
curl "http://localhost:8000/documentation/openapi.json?api_type=unknown"
# {"message":"The given data was invalid.", ...}
```

### How to Test

Add the API type to config and retry:

```bash theme={null}
curl "http://localhost:8000/documentation/openapi.json?api_type=partner"
# Returns filtered OpenAPI spec
```

***

## Template JSON Parsing Failures

### Symptom

Generation fails with JSON template errors:

```text theme={null}
❌ Failed to generate specification
JSON parse error: Syntax error in /path/to/templates/generic/list.json
```

### Cause

Invalid JSON syntax in `resources/openapi/templates` or missing required fields.

### Mitigation

<Steps>
  <Step title="Validate template JSON">
    Use a JSON validator:

    ```bash theme={null}
    jq . resources/openapi/templates/generic/list.json
    ```

    Or online: [jsonlint.com](https://jsonlint.com)
  </Step>

  <Step title="Enable output validation">
    In `config/openapi-templates.php`:

    ```php theme={null}
    'rendering' => [
        'validate_output' => true,
    ],
    ```

    This adds extra validation but slows generation. Use only for debugging.
  </Step>

  <Step title="Check for common issues">
    * **Trailing commas**: Not allowed in JSON
    * **Unescaped quotes**: Must escape `"` inside strings
    * **Missing braces**: Ensure all `{` have matching `}`
  </Step>
</Steps>

### How to Reproduce

Create invalid JSON in a template:

```json theme={null}
{
  "summary": "List {{entity_plural}}",
  "description": "Missing closing brace"
```

```bash theme={null}
php artisan openapi:generate
# ❌ Failed to generate specification
# JSON parse error...
```

### How to Test

Fix JSON and regenerate:

```json theme={null}
{
  "summary": "List {{entity_plural}}",
  "description": "Valid JSON"
}
```

```bash theme={null}
php artisan openapi:generate
# ✅ OpenAPI specification generated!
```

***

## General Troubleshooting

<AccordionGroup>
  <Accordion title="No routes found matching filters">
    **Cause**: Routes don't match API type prefixes or are excluded.

    **Solution**:

    ```bash theme={null}
    # Check route list
    php artisan route:list --path=admin

    # Verify API type prefix
    config('openapi.api_types.admin.prefix')

    # Check exclusion patterns
    config('openapi.exclude_routes')
    ```
  </Accordion>

  <Accordion title="Placeholders not replaced in output">
    **Cause**: Template placeholders don't match available variables.

    **Solution**:

    Available placeholders:

    * `{{entity_singular}}` - e.g., "user"
    * `{{entity_plural}}` - e.g., "users"
    * `{{api_type}}` - e.g., "admin"

    Ensure template uses correct placeholder syntax.
  </Accordion>

  <Accordion title="FormRequest validation rules not appearing">
    **Cause**: Route doesn't type-hint FormRequest in controller method.

    **Solution**:

    ```php theme={null}
    // ❌ Rules not extracted
    public function store(Request $request) { ... }

    // ✅ Rules extracted automatically
    public function store(StoreUserRequest $request) { ... }
    ```
  </Accordion>

  <Accordion title="Permission denied writing output files">
    **Cause**: Output directory not writable.

    **Solution**:

    ```bash theme={null}
    chmod -R 775 storage/app/public/openapi
    chown -R www-data:www-data storage/app/public/openapi
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/public-api">
    Explore the programmatic API for custom integrations.
  </Card>

  <Card title="Configuration Reference" icon="gear" href="/configuration/main-config">
    Detailed configuration options and defaults.
  </Card>
</CardGroup>
