# Data flow — microsoft/markitdown

> Canonical page: https://codevetter.com/unpack/markitdown/data-flow

# Data flow — microsoft/markitdown

Scanned commit b8f79c57ebc0044be41323d89b2a45d3fda8460e (2026-09-21). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Data enters through convert()/convert_local()/convert_stream()/convert_uri()/convert_response(), is normalized to a seekable BinaryIO plus a list of StreamInfo guesses, then fanned out to priority-sorted converters. Type detection is layered: caller hints and headers produce a base StreamInfo, mimetypes fills gaps, then magika identifies content from bytes and charset-normalizer guesses text encoding; incompatible guesses produce two candidate StreamInfos tried in order. The winning converter returns a DocumentConverterResult whose markdown is normalized (trailing whitespace stripped, 3+ newlines collapsed) before returning.
- convert() is a type-based dispatcher: str with scheme http/https/file/data routes to convert_uri, other strings and Path to convert_local, requests.Response to convert_response, and readable non-TextIO objects to convert_stream; anything else raises TypeError. — [packages/markitdown/src/markitdown/_markitdown.py#L322-366](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L322-L366)
- convert_local() seeds StreamInfo with local_path, os.path.splitext extension, and basename, merges caller stream_info (plus deprecated file_extension/url kwargs), opens the file 'rb', guesses stream info, then calls _convert. — [packages/markitdown/src/markitdown/_markitdown.py#L368-403](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L368-L403)
- convert_stream() buffers non-seekable streams fully into io.BytesIO (4 KiB chunks) before guessing — the entire file is resident in memory from that point on. — [packages/markitdown/src/markitdown/_markitdown.py#L435-450](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L435-L450)
- convert_uri() branches by scheme: file: URIs are resolved by file_uri_to_path (rejecting non-localhost netlocs and UNC/device paths), data: URIs are decoded by parse_data_uri (base64 or percent), and http(s): is fetched with self._requests_session.get(uri, stream=True) then converted via convert_response. — [packages/markitdown/src/markitdown/_markitdown.py#L471-531](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L471-L531) [packages/markitdown/src/markitdown/_uri_utils.py#L15-75](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_uri_utils.py#L15-L75)
- convert_response() builds the base StreamInfo from Content-Type (mimetype + charset), Content-Disposition filename (parsed via email.message.Message, RFC 2231-aware), or the URL path extension, then buffers the whole body via response.iter_content(chunk_size=512) into BytesIO. — [packages/markitdown/src/markitdown/_markitdown.py#L533-604](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L533-L604) [packages/markitdown/src/markitdown/_markitdown.py#L56-70](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L56-L70)
- _get_stream_info_guesses() enhances the base guess via the mimetypes module, then calls magika.identify_stream; for text predictions it samples up to 64 KiB (completing a split trailing UTF-8 char via _read_charset_sample) and runs charset_normalizer.from_bytes().best() for charset detection. — [packages/markitdown/src/markitdown/_markitdown.py#L741-840](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L741-L840) [packages/markitdown/src/markitdown/_markitdown.py#L73-98](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L73-L98)
- When magika's prediction is incompatible with caller-supplied mimetype/extension/charset, both the enhanced base guess and the magika guess are appended as separate StreamInfo candidates, each tried in sequence against all converters in _convert. — [packages/markitdown/src/markitdown/_markitdown.py#L787-833](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L787-L833) [packages/markitdown/src/markitdown/_markitdown.py#L622](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L622)
- _convert() iterates 'stream_info_guesses + [StreamInfo()]' outer loop and priority-sorted converters inner loop, asserting the stream position is unchanged by accepts(); on convert() success it normalizes text_content (rstrip each line, collapse >=3 newlines to 2) and returns immediately. — [packages/markitdown/src/markitdown/_markitdown.py#L606-690](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L606-L690)
- Per-call kwargs are enriched in _convert with global llm_client/llm_model/llm_prompt/style_map/exiftool_path, a private _parent_converters list for nested processing, and legacy file_extension/url keys derived from the current StreamInfo guess. — [packages/markitdown/src/markitdown/_markitdown.py#L630-657](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L630-L657)
- Converters signal outcomes via exceptions: convert() failures are collected as FailedConversionAttempt and, if no converter succeeds, raised as FileConversionException (with per-converter trace summary); if no converter even accepted, UnsupportedFormatException is raised. — [packages/markitdown/src/markitdown/_markitdown.py#L671-699](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L671-L699) [packages/markitdown/src/markitdown/_exceptions.py#L42-82](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_exceptions.py#L42-L82)
- Office/HTML converters converge on HTML as an intermediate: DocxConverter (mammoth), EpubConverter (spine files), PptxConverter and RSS/Wikipedia converters produce HTML that is finalized by HtmlConverter -> _CustomMarkdownify, which also strips javascript links and truncates data URIs unless keep_data_uris is set. — [packages/markitdown/src/markitdown/converters/_docx_converter.py#L113-123](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_docx_converter.py#L113-L123) [packages/markitdown/src/markitdown/converters/_html_converter.py#L42-91](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_html_converter.py#L42-L91) [packages/markitdown/src/markitdown/converters/_markdownify.py#L25-120](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_markdownify.py#L25-L120)
- In the MCP server, each convert_to_markdown call constructs a fresh MarkItDown instance (plugins gated by env MARKITDOWN_ENABLE_PLUGINS) and calls convert_uri; every exception type is mapped to a ToolError that deliberately omits internal detail (e.g., FileConversionException becomes 'File conversion failed.', OSError reports only os.strerror). — [packages/markitdown-mcp/src/markitdown_mcp/__main__.py#L19-55](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown-mcp/src/markitdown_mcp/__main__.py#L19-L55)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
