microsoft/semantic-kernel · error · RuntimeError
Invalid filename: would write outside the expected directory
Error message
Invalid filename: would write outside the expected directory
What it means
Same path-traversal guard as error 626, in the streaming Bedrock sample. After streaming, the sample composes output_dir / binary_item.metadata['name'], resolves it, and raises RuntimeError if the resolved path is not within the script directory. It blocks writes that would escape via '..', absolute paths, or symlinked names.
Solutions
- Reduce the name to its basename with os.path.basename before joining.
- Regenerate the chart asking the model for a simple filename if it returned a path-like name.
- Write to a dedicated safe output subdir using a generated name.
- Keep the is_relative_to check as a secondary guard.
Example fix
# before
output_dir = Path(__file__).parent.resolve()
file_path = (output_dir / binary_item.metadata["name"]).resolve()
if not file_path.is_relative_to(output_dir):
raise RuntimeError("Invalid filename: would write outside the expected directory")
# after
import os
file_path = (output_dir / os.path.basename(binary_item.metadata["name"])).resolve()
if not file_path.is_relative_to(output_dir):
raise RuntimeError("Invalid filename: would write outside the expected directory") Defensive patterns
Strategy: validation
Validate before calling
import os
from pathlib import Path
def safe_join(output_dir: Path, name: str) -> Path:
safe = os.path.basename(str(name))
target = (output_dir / safe).resolve()
if not target.is_relative_to(output_dir.resolve()):
raise RuntimeError(f"Refusing to write outside {output_dir}: {name!r}")
return target Type guard
def is_safe_filename(name: object) -> bool:
s = str(name or "")
return bool(s) and os.path.basename(s) == s and "/" not in s and "\\" not in s and ".." not in s Try / catch
try:
file_path = safe_join(output_dir, binary_item.metadata["name"])
binary_item.write_to_file(file_path)
except RuntimeError as e:
logger.error("Refused unsafe write: %s", e) Prevention
- Reduce streamed filenames to basename before composing paths.
- Keep the is_relative_to guard after resolve().
- Write to a dedicated safe output directory using a generated name.
When it happens
Trigger: binary_item.metadata['name'] (from the streamed BinaryContent) resolves outside Path(__file__).parent - e.g. '../../x', '/abs/path', or a name with path separators that climbs out of the directory.
Common situations: The model/code-interpreter returns a file name containing path separators or an absolute path; a crafted/malformed metadata name; or running the sample in an environment where __file__ resolves oddly.
Related errors
- Invalid filename: would write outside the expected directory
- No chart generated
- City ' ' is not in the list of cities
- No chart generated
- Streaming chat message contents not implemented.
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/63dc5a3a2d444aba.
Report an issue: GitHub.
Appendix: source
Thrown at python/samples/concepts/agents/bedrock_agent/bedrock_agent_with_code_interpreter_streaming.py:69
binary_item = next((item for item in response.items if isinstance(item, BinaryContent)), None)
print()
finally:
# Delete the agent
await bedrock_agent.delete_agent()
await thread.delete() if thread else None
# Save the chart to a file
if not binary_item:
raise RuntimeError("No chart generated")
# Securely assemble the file path and validate it's within the expected directory
# This is a defense-in-depth measure against directory traversal attacks
output_dir = Path(__file__).parent.resolve()
file_path = (output_dir / binary_item.metadata["name"]).resolve()
# Verify the resolved path is within the expected directory
if not file_path.is_relative_to(output_dir):
raise RuntimeError("Invalid filename: would write outside the expected directory")
binary_item.write_to_file(file_path)
print(f"Chart saved to {file_path}")
# Sample output (using anthropic.claude-3-haiku-20240307-v1:0):
# Response:
# Here is the bar chart for the given data:
# [A bar chart showing the following data:
# Panda 5
# Tiger 8
# Lion 3
# Monkey 6
# Dolpin 2]
# Chart saved to ...
if __name__ == "__main__":
asyncio.run(main())View on GitHub (pinned to c028a0c7dc)