ZhuLinsen/daily_stock_analysis · error · GenerationError

unsafe_config

unsafe_config

Error message

unsafe_config at configuration for backend {backend}

What it means

A structured GenerationError with code unsafe_config raised at the configuration stage when resolve_local_cli_preset cannot find the requested preset_id in SAFE_LOCAL_CLI_PRESETS. Presets are an explicit allowlist of vetted local CLI configurations; unknown IDs are rejected as unsafe rather than silently defaulted. It is non-retryable and non-fallbackable by design.

Source

Thrown at src/llm/local_cli_backend.py:2040

        for event in events:
            event_type_lower = str(event.get("type") or "").strip().lower()
            if (
                _opencode_blocked_event_reason(event, event_type_lower)
                or bool(event.get("error"))
                or event.get("is_error") is True
            ):
                return True
    except LocalCliExtractionError:
        return False
    return False


def resolve_local_cli_preset(preset_id: str) -> LocalCliPreset:
    """Return a safe preset or raise a structured unsafe_config error."""

    preset = SAFE_LOCAL_CLI_PRESETS.get((preset_id or "").strip().lower())
    if preset is None:
        raise GenerationError(
            error_code=GenerationErrorCode.UNSAFE_CONFIG,
            stage="configuration",
            retryable=False,
            fallbackable=False,
            backend=preset_id or "local_cli",
            provider=preset_id or "local_cli",
            details={
                "reason": "unknown_local_cli_preset",
                "preset_id": preset_id,
                "allowed_presets": sorted(SAFE_LOCAL_CLI_PRESETS),
            },
        )
    return preset


class LocalCliGenerationBackend(GenerationBackend):
    """Restricted subprocess-backed generation backend."""

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Read the allowed_presets list from the error details (or inspect SAFE_LOCAL_CLI_PRESETS) and set the preset ID to one of those exact values
  2. Fix typos, casing, and stray whitespace in the configured preset ID (normalization only handles case and surrounding whitespace)
  3. If the preset was renamed, update the config to the new identifier from the current codebase
  4. Upgrade or pin the deployment so its allowlist matches the preset the config references

Example fix

# before
LOCAL_CLI_PRESET=claudecode

# after
LOCAL_CLI_PRESET=claude-code  # must match an entry in SAFE_LOCAL_CLI_PRESETS
Defensive patterns

Strategy: validation

Validate before calling

from src.llm.local_cli_backend import SAFE_LOCAL_CLI_PRESETS

def is_known_preset(preset_id: str) -> bool:
    return (preset_id or "").strip().lower() in SAFE_LOCAL_CLI_PRESETS

Type guard

from typing import Any
from src.llm.local_cli_backend import SAFE_LOCAL_CLI_PRESETS

def is_safe_preset_id(value: Any) -> bool:
    return isinstance(value, str) and value.strip().lower() in SAFE_LOCAL_CLI_PRESETS

Try / catch

try:
    preset = resolve_local_cli_preset(preset_id)
except GenerationError as exc:
    if exc.error_code == GenerationErrorCode.UNSAFE_CONFIG:
        log.error("Unknown preset %r; allowed: %s", preset_id, exc.details["allowed_presets"])
    raise

Prevention

When it happens

Trigger: Calling resolve_local_cli_preset('claude-code') (or any ID not in the allowlist), passing an empty or misspelled preset string, or passing a casing/whitespace variant not covered by the .strip().lower() normalization. The details payload includes reason=unknown_local_cli_preset and the sorted list of allowed presets.

Common situations: A config file or env var names a preset removed in a refactor; user typos in LOCAL_CLI_PRESET; version mismatch where the deployed code has a smaller allowlist than the config expects; copy-pasting a preset ID from newer docs into an older install.

Related errors


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