unclecode/crawl4ai · info · Error

Invalid config

Error message

Invalid config

What it means

ArtifactNotFound raised when the artifact file exists and is regular but its mtime is older than ARTIFACT_TTL_SECONDS (default 3600 s). The file is unlinked (best-effort) and the lookup fails — artifacts are short-lived by design, and expiry is hidden behind the same not-found error.

Source

Thrown at deploy/docker/static/playground/index.html:580

        async function pyConfigToJson() {
            const code = cm.getValue().trim();
            if (!code) return {};

            // Server requires `type` alongside `code` (CrawlerRunConfig | BrowserConfig).
            // The UI already tracks this in #cfg-type; omitting it always 400s Advanced Config.
            const cfgType = document.getElementById('cfg-type').value;
            const res = await authFetch('/config/dump', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ type: cfgType, code }),
            });

            const statusEl = document.getElementById('cfg-status');
            if (!res.ok) {
                const msg = await res.text();
                statusEl.textContent = '✖ config error';
                statusEl.className = 'text-xs text-red-400';
                throw new Error(msg || 'Invalid config');
            }

            statusEl.textContent = '✓ parsed';
            statusEl.className = 'text-xs text-green-400';

            return await res.json();
        }

        // ================ SERVER COMMUNICATION ================

        // Update status UI
        function updateStatus(status, time, memory, peakMemory) {
            const statusEl = document.getElementById('execution-status');
            const badgeEl = document.querySelector('#status-badge span:first-child');
            const textEl = document.querySelector('#status-badge span:last-child');

            statusEl.classList.remove('hidden');
            badgeEl.className = 'w-3 h-3 rounded-full mr-2';

View on GitHub (pinned to 7e80152142)

Solutions

  1. Retrieve artifacts promptly — within CRAWL4AI_ARTIFACT_TTL_SECONDS of creation
  2. Persist important artifacts to your own storage immediately after the crawl response arrives
  3. Raise CRAWL4AI_ARTIFACT_TTL_SECONDS on the server if longer retrieval windows are required

Example fix

# after crawl, download the artifact immediately instead of caching the id
meta = write_artifact("screenshot", data)
# fetch /artifact/{meta['artifact_id']} now, or store `data` yourself
Defensive patterns

Strategy: retry

Validate before calling

import time
TTL = int(os.environ.get("CRAWL4AI_ARTIFACT_TTL_SECONDS", 3600))
def artifact_likely_alive(meta, now=None) -> bool:
    return (now or time.time()) - meta["created_at"] < TTL - 60  # 1 min slack

Try / catch

try:
    path, mime = resolve_artifact(meta["artifact_id"])
except ArtifactNotFound:
    meta = await regenerate_artifact(...)  # expired -> re-crawl and re-store
    path, mime = resolve_artifact(meta["artifact_id"])

Prevention

When it happens

Trigger: Fetching /artifact/{id} more than an hour (or configured TTL) after it was written; a pause between crawl and retrieval longer than the TTL; low TTL deployments.

Common situations: Retrying a downstream pipeline hours later; queuing artifact downloads behind slow workers; crawls run at night with retrieval the next morning.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/0ffb3a336aeaac24. Report an issue: GitHub.