666ghj/MiroFish · error · ValueError

Ontology result must be an object

Error message

Ontology result must be an object

What it means

Raised in OntologyGenerator._validate_and_process when the parsed LLM response for ontology generation is not a JSON object (dict). The generator asks the LLM for a JSON object with entity_types/edge_types/analysis_summary; if parsing yields a list, a bare string, or null (e.g. the model returned a JSON array, a markdown-fenced scalar, or empty content), this ValueError fires before any field extraction.

Source

Thrown at backend/app/services/ontology_generator.py:435

        """长分块保留首尾,避免每个分块内部再次变成只看开头。"""

        text = text.strip()
        if len(text) <= char_limit:
            return text

        marker = "\n...(本分块中间内容省略)...\n"
        if char_limit <= len(marker) + 20:
            return text[:char_limit]

        remaining = char_limit - len(marker)
        head_len = remaining // 2
        tail_len = remaining - head_len
        return f"{text[:head_len].rstrip()}{marker}{text[-tail_len:].lstrip()}"
    
    def _validate_and_process(self, result: Dict[str, Any]) -> Dict[str, Any]:
        """验证和后处理结果"""
        if not isinstance(result, dict):
            raise ValueError("Ontology result must be an object")

        raw_entities = result.get("entity_types")
        raw_edges = result.get("edge_types")
        if not isinstance(raw_entities, list):
            raw_entities = []
        if not isinstance(raw_edges, list):
            raw_edges = []
        if not isinstance(result.get("analysis_summary"), str):
            result["analysis_summary"] = ""

        # Normalize entity entries before touching their fields. LLMs
        # occasionally emit a bare string, null, or another scalar.
        entity_name_map: Dict[str, str] = {}
        processed_entities: List[Dict[str, Any]] = []
        seen_entity_names = set()
        for raw_entity in raw_entities:
            if isinstance(raw_entity, str):
                entity = {"name": raw_entity}

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Retry generation with the same prompt — non-dict output is often stochastic; add explicit 'respond with a single JSON object' instruction and an example in the prompt.
  2. If the model consistently returns a list, wrap/normalize: accept a top-level list by mapping it to {"entity_types": result} if that matches intent.
  3. Increase max_tokens to avoid truncation, and use JSON mode / response_format=json_object when the provider supports it.
  4. Log the raw LLM output on failure to see exactly which shape the model emitted before patching.

Example fix

# before
result = json.loads(raw)
processed = gen._validate_and_process(result)
# after
result = json.loads(raw)
if isinstance(result, list):
    result = {"entity_types": result}
processed = gen._validate_and_process(result)
Defensive patterns

Strategy: type-guard

Type guard

def is_ontology_object(result: object) -> bool:
    return isinstance(result, dict)

Try / catch

result = parse_llm_json(raw)
if not isinstance(result, dict):
    if isinstance(result, list):
        result = {"entity_types": result}  # normalize observed LLM shape
    else:
        result = regenerate_with_stricter_prompt()  # one retry, then fail
processed = gen._validate_and_process(result)

Prevention

When it happens

Trigger: LLM returns a JSON array of entities instead of an object; model outputs prose or a code block whose parsed JSON is a scalar; response truncated so the parser produced a non-dict fragment; weaker model ignoring the schema prompt.

Common situations: Switching to a smaller/cheaper model that ignores output-format instructions; prompts edited so the requested shape drifted; max_tokens set too low causing truncation and salvage-parsing; some providers returning top-level arrays by convention.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/69bdc58b349b6a75. Report an issue: GitHub.