{"record":{"id":"07e44ac8fa036ef1","repo":"langchain-ai/deepagents","slug":"file-content-must-be-a-string-or-a-legacy-list-of","errorCode":null,"errorMessage":"File content must be a string or a legacy list of strings, got {type(content).__name__}.","messagePattern":"File content must be a string or a legacy list of strings, got (.+?)\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/utils.py","lineNumber":196,"sourceCode":"\ndef _normalize_content(file_data: FileData) -> str:\n    \"\"\"Normalize current and legacy file data content to a plain string.\n\n    Args:\n        file_data: `FileData` dict with `content` key.\n\n    Returns:\n        Content as a single string.\n\n    Raises:\n        TypeError: If content is neither a string nor a legacy list of strings.\n    \"\"\"\n    content: object = file_data[\"content\"]\n    if isinstance(content, list) and all(isinstance(line, str) for line in content):\n        return \"\\n\".join(content)\n    if not isinstance(content, str):\n        msg = f\"File content must be a string or a legacy list of strings, got {type(content).__name__}.\"\n        raise TypeError(msg)\n    return content\n\n\ndef sanitize_tool_call_id(tool_call_id: str) -> str:\n    r\"\"\"Sanitize tool_call_id to prevent path traversal and separator issues.\n\n    Replaces dangerous characters (., /, \\) with underscores.\n    \"\"\"\n    return tool_call_id.replace(\".\", \"_\").replace(\"/\", \"_\").replace(\"\\\\\", \"_\")\n\n\ndef format_content_with_line_numbers(\n    content: str | list[str],\n    start_line: int = 1,\n) -> str:\n    \"\"\"Format file content with line numbers.\n\n    Chunks lines longer than `MAX_LINE_LENGTH` with continuation markers","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/utils.py#L178-L214","documentation":"`_normalize_content` (used by `file_data_to_string` and grep matching) accepts file content as a `str` or the legacy `list[str]` line format, which it joins with newlines. Any other content type in a `FileData` dict raises a TypeError naming the actual type, protecting downstream string operations (grep, read) from bytes/dict/None content.","triggerScenarios":"Providing `FileData` dicts with `content` set to bytes, dict, None, or a mixed list to backend utils that call `file_data_to_string` or `grep_matches_from_files`; custom backends returning non-string content from their own storage layer.","commonSituations":"Custom BackendProtocol implementations reading raw bytes from disk/S3; legacy store data migrated with mixed-type lists; hand-built FileData in tests.","solutions":["Have the custom backend decode content to `str` before building FileData","Join lines client-side (`'\\n'.join(lines)`) when your storage returns a list","Skip or repair FileData entries with non-conforming content before grepping","Normalize mixed lists with `[str(line) for line in lines]`"],"exampleFix":"// before\n{'content': path.read_bytes(), ...}\n// after\n{'content': path.read_text(encoding='utf-8'), ...}","handlingStrategy":"type-guard","validationCode":"def to_file_data(path: str, raw) -> dict:\n    if isinstance(raw, bytes):\n        raw = raw.decode('utf-8')\n    if isinstance(raw, list) and not all(isinstance(l, str) for l in raw):\n        raw = [str(l) for l in raw]\n    if not isinstance(raw, str):\n        raw = str(raw)\n    return {'content': raw, 'encoding': 'utf-8'}","typeGuard":"def is_stringable_file_content(content: object) -> bool:\n    if isinstance(content, str):\n        return True\n    return isinstance(content, list) and all(isinstance(l, str) for l in content)","tryCatchPattern":"try:\n    text = file_data_to_string(file_data)\nexcept TypeError as exc:\n    if 'must be a string or a legacy list of strings' in str(exc):\n        text = str(file_data['content'])\n    else:\n        raise","preventionTips":["Decode to str at the storage boundary (read_text, response.text) before building FileData","Keep legacy list[str] line data homogeneous; normalize mixed lists on write","Add a read-side assert/guard in custom backends before returning FileData","Cover custom backends with a test asserting FileData content is str or list[str]"],"tags":["type-error","file-data","validation"],"backgroundTag":"invalid-file-content-type","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}