ZhuLinsen/daily_stock_analysis · error · IntelligenceServiceError

source url is required

Error message

source url is required

What it means

Validation in _normalize_source_fields: the url field, after str()/strip(), is empty. Note the check is only for non-emptiness here — the separate _validate_url call in create_source handles well-formedness.

Source

Thrown at src/services/intelligence_service.py:372

                cls._auto_fetch_last_run_at = datetime.now()
                cls._auto_fetch_last_result = dict(result)
                cls._auto_fetch_in_progress = False
                cls._auto_fetch_condition.notify_all()
        return result

    def _normalize_source_fields(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        name = str(payload.get("name") or "").strip()
        url = str(payload.get("url") or "").strip()
        source_type = str(payload.get("source_type") or "rss").strip().lower()
        scope_type = str(payload.get("scope_type") or "market").strip().lower()
        scope_value = str(payload.get("scope_value") or "").strip() or None
        market = str(payload.get("market") or "cn").strip().lower()
        enabled = bool(payload.get("enabled", True))
        description = str(payload.get("description") or "").strip() or None
        if not name:
            raise IntelligenceServiceError("source name is required")
        if not url:
            raise IntelligenceServiceError("source url is required")
        if source_type not in _ALLOWED_SOURCE_TYPES:
            raise IntelligenceServiceError(f"unsupported source_type: {source_type}")
        if scope_type not in _ALLOWED_SCOPE_TYPES:
            raise IntelligenceServiceError(f"unsupported scope_type: {scope_type}")
        if scope_type in {"symbol", "sector"} and not scope_value:
            raise IntelligenceServiceError(f"scope_value is required when scope_type={scope_type}")
        if market not in _ALLOWED_MARKETS:
            raise IntelligenceServiceError(f"unsupported market: {market}")
        return {
            "name": name[:100],
            "source_type": source_type,
            "url": url,
            "enabled": enabled,
            "scope_type": scope_type,
            "scope_value": scope_value[:64] if scope_value else None,
            "market": market,
            "description": description,
        }

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Provide a non-empty URL string (e.g. the RSS/atom feed endpoint)
  2. Check the payload key is exactly 'url'
  3. Client-side: mark URL required in the create-source form

Example fix

# before
svc.create_source({'name': 'news', 'url': ''})  # -> error
# after
svc.create_source({'name': 'news', 'url': 'https://example.com/feed.xml'})
Defensive patterns

Strategy: validation

Validate before calling

url = str(payload.get('url') or '').strip()
if not url:
    raise UserError('URL 不能为空')

Type guard

def has_source_url(payload: dict) -> bool:
    return bool(str(payload.get('url') or '').strip())

Try / catch

try:
    svc.create_source(payload)
except IntelligenceServiceError as e:
    if 'url is required' in str(e):
        focus_field('url')
    else:
        raise

Prevention

When it happens

Trigger: POSTing a source with url omitted, empty, or whitespace-only; template override setting url='' or None being stripped to empty.

Common situations: Form missing the URL field; API payload typo like 'urls' instead of 'url'; template default url empty.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/a8a9c33bb8720665. Report an issue: GitHub.