microsoft/autogen · error · ValueError
Expected str or bytes, got {type(new_content)}
Error message
Expected str or bytes, got {type(new_content)} What it means
add_or_update_file accepts only str or bytes (bytes is UTF-8 decoded first). Any other type (int, dict, list, None) is rejected with this ValueError to keep every revision a clean string.
Source
Thrown at python/packages/autogen-ext/src/autogen_ext/memory/canvas/_text_canvas.py:113
# ----------------------------------------------------------------------------------
# BaseCanvas interface implementation
# ----------------------------------------------------------------------------------
def list_files(self) -> Dict[str, int]:
"""Return a mapping of *filename → latest revision number*."""
return {fname: revs[-1].revision for fname, revs in self._files.items() if revs}
def get_latest_content(self, filename: str) -> str: # noqa: D401 – keep API identical
"""Return the most recent content or an empty string if the file is new."""
revs = self._files.get(filename, [])
return revs[-1].content if revs else ""
def add_or_update_file(self, filename: str, new_content: Union[str, bytes, Any]) -> None:
"""Create *filename* or append a new revision containing *new_content*."""
if isinstance(new_content, bytes):
new_content = new_content.decode("utf-8")
if not isinstance(new_content, str):
raise ValueError(f"Expected str or bytes, got {type(new_content)}")
if filename not in self._files:
self._files[filename] = [FileRevision(new_content, 1)]
else:
last_rev_num = self._files[filename][-1].revision
self._files[filename].append(FileRevision(new_content, last_rev_num + 1))
def get_diff(self, filename: str, from_revision: int, to_revision: int) -> str:
"""Return a unified diff between *from_revision* and *to_revision*."""
revisions = self._files.get(filename, [])
if not revisions:
return ""
# Fetch the contents for the requested revisions.
from_content = self.get_revision_content(filename, from_revision)
to_content = self.get_revision_content(filename, to_revision)
if from_content == "" and to_content == "": # one (or both) revision ids not found
return ""
diff = difflib.unified_diff(
from_content.splitlines(keepends=True),View on GitHub (pinned to 027ecf0a37)
Solutions
- Serialize non-string content to a string before calling: json.dumps for dicts/lists, str() for scalars.
- If you passed bytes with a non-UTF-8 encoding, decode manually first.
Example fix
# before
canvas.add_or_update_file("data.json", {"a": 1})
# after
canvas.add_or_update_file("data.json", json.dumps({"a": 1})) Defensive patterns
Strategy: type-guard
Validate before calling
import json
if not isinstance(content, (str, bytes)):
content = json.dumps(content) # or str(content) for scalars
canvas.add_or_update_file(filename, content) Type guard
def is_canvas_writable_content(value) -> bool:
return isinstance(value, (str, bytes)) Prevention
- Serialize at the boundary: json.dumps any structured payload before it reaches the canvas.
- Type the parameter as str in your own wrappers so mypy catches misuse.
When it happens
Trigger: Calling add_or_update_file(filename, 123), add_or_update_file(filename, {'a': 1}), or passing None/Path objects as content.
Common situations: LLM tool calls returning non-string payloads; upstream code passing parsed JSON or numbers directly; forgetting to serialize before storage.
Related errors
- Expected str or bytes, got {type(patch_data)}
- Expected Memory, List[Memory], or None, got {type(memory)}
- Unsupported tool type: {type(tool)}
- Unsupported handoff type: {type(handoff)}
- Message type must be a string, got {type(message_type)}
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/44d3a7d8725cdd98.
Report an issue: GitHub.