nexu-io/open-design · error · ValueError
slide part is missing
Error message
slide part is missing: {resolved} What it means
Raised in _ordered_slide_parts (pptx_qa.py) immediately after the relationship check: once a slide relationship Target is resolved into a part path, the code confirms that path exists in the .pptx zip via `zf.namelist()`. If the slide XML part referenced by the relationship is absent from the archive, it raises ValueError. This catches packages whose relationships point at parts that were never packaged.
Solutions
- List the archive contents to see which slide parts exist: `unzip -l deck.pptx | grep slides/slide` and compare against the rels targets.
- Round-trip the file through PowerPoint/Keynote to reconcile presentation.xml with the packaged parts, then retry.
- Regenerate the deck from the upstream producer (python-pptx script, exporter) rather than patching the broken zip by hand.
- If only a few slides are missing, accept that the deck is damaged and run pptx_qa on a known-good backup copy.
Example fix
// before python3 pptx_qa.py inspect deck.pptx # -> ValueError: slide part is missing: ppt/slides/slide5.xml // after unzip -l deck.pptx | grep slide # confirm which parts exist # regenerate/re-save deck, then: python3 pptx_qa.py inspect deck.pptx
Defensive patterns
Strategy: try-catch
Validate before calling
import zipfile
def all_slide_parts_present(path: str) -> bool:
with zipfile.ZipFile(path) as zf:
names = set(zf.namelist())
try:
from pptx_qa import _ordered_slide_parts # reuse the resolver
for part in _ordered_slide_parts(zf):
if part not in names:
return False
except ValueError:
return False
return True Try / catch
try:
parts = _ordered_slide_parts(zf)
except ValueError as exc:
raise SystemExit(f"Deck is missing slide parts: {exc}. Re-export from the source tool.") from exc Prevention
- Confirm slide parts exist in the archive before parsing: `unzip -l deck.pptx | grep slides/slide`.
- Re-export decks from the original tool rather than patching the zip.
- When accepting user uploads, validate package integrity up front and reject corrupt files with a clear message.
- Cross-check rels Targets against namelist() to catch dangling references early.
When it happens
Trigger: Running pptx_qa on a .pptx where presentation.xml.rels has a Target like 'slides/slide5.xml' but the archive contains no such entry; or where Target resolved through _resolve_target lands on a path not in namelist(). Common with stripped/slimmed packages or partially extracted decks.
Common situations: A tool that rewrote relationships without re-zipping the corresponding slide XML; file extraction/repackaging that dropped slide parts; a deck that was edited to remove slides but whose presentation.xml still lists them; cross-package Target resolution bugs in converters producing absolute or wrongly-relative paths.
Related errors
- slide relationship is missing for
- brief mode reads markdown/text raw material, not rendered…
- invalid zip central directory
- invalid zip central directory entry
- zip contains no files
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/68f10eddaec58809.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/community/humanize-ppt/scripts/pptx_qa.py:89
if target.startswith("/"):
return target.lstrip("/")
return posixpath.normpath(posixpath.join(posixpath.dirname(source_part), target))
def _ordered_slide_parts(zf: zipfile.ZipFile) -> list[str]:
presentation = "ppt/presentation.xml"
root = _read_xml(zf, presentation)
rels = _relationships(zf, presentation)
parts: list[str] = []
for slide_id in root.findall(".//p:sldIdLst/p:sldId", NS):
rel_id = slide_id.attrib.get(f"{{{REL}}}id")
rel = rels.get(rel_id or "", {})
target = rel.get("Target")
if not target:
raise ValueError(f"slide relationship is missing for {rel_id or 'unknown id'}")
resolved = _resolve_target(presentation, target)
if resolved not in zf.namelist():
raise ValueError(f"slide part is missing: {resolved}")
parts.append(resolved)
return parts
def _text(root: ET.Element) -> str:
return " ".join(
(node.text or "").strip()
for node in root.findall(".//a:t", NS)
if (node.text or "").strip()
)
def _tokens(value: str) -> set[str]:
return {
token.lower()
for token in re.findall(r"[A-Za-z0-9]+|[\u3400-\u9fff]", value)
if token.strip()
}View on GitHub (pinned to 5be4028344)