This is a technical decision aid. Confirm the current extension contract and validator evidence before implementation.
python-pptx cannot create combo charts. The official documentation states it "doesn't yet support creating multi-plot charts." Issue #338 has been open since November 2017 — 8 years without resolution. The commonly cited XML splicing workaround triggers PowerPoint's repair dialog. The workaround that actually works is switching to a library that supports combo charts natively: PptxGenJS (JavaScript, free) or Runstamp (JSON, Pro tier).
Why can't python-pptx make multi-plot charts?
A combo chart overlays two chart types on the same axes — typically bars for one metric and a line for another. Think revenue (bars) overlaid with growth rate (line). According to the python-pptx chart documentation, "python-pptx doesn't yet support creating multi-plot charts, but you can access multiple plots on a chart that already has them."
That second clause is the source of the workaround — and the source of the problem. python-pptx can read combo charts from existing files. Developers have tried to exploit this by creating two separate chart objects, extracting the XML from one, and inserting it into the other. The result: PowerPoint's repair dialog.
python-pptx supports 8 chart element types
for creation: area, bar, bubble, doughnut, line, pie, radar, and scatter. According to the chart overview documentation, 29 of 73 PowerPoint chart types are supported for creation, with 44 remaining — including all 3D charts, stock charts, and surface charts. According to issue #583 (December 2019), Office 2016+ chart types (waterfall, histogram, treemap, sunburst, box and whisker) use a different XML namespace (cx: instead of c:) and are not supported at all.
The XML workaround (and why it fails)
According to issue #338, the original reporter tried the following approach:
from pptx import Presentation
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Inches
# create column chart in presentation 1
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
chart_data = ChartData()
chart_data.categories = ["East", "West", "Midwest"]
chart_data.add_series("Q1 Sales", (19.2, 21.4, 16.7))
x, y, cx, cy = Inches(2), Inches(2), Inches(6), Inches(4.5)
columnc = slide.shapes.add_chart(
XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, chart_data
)
# create line chart in presentation 2
prs1 = Presentation()
slide1 = prs1.slides.add_slide(prs1.slide_layouts[6])
chart_data1 = ChartData()
chart_data1.categories = ["East", "West", "Midwest"]
chart_data1.add_series("Growth", (5, 5, 5))
linec = slide1.shapes.add_chart(
XL_CHART_TYPE.LINE, x, y, cx, cy, chart_data1
)
# splice line chart XML into column chart's plot area
chartb = columnc.chart
chartl = linec.chart
a = chartl.plots._plotArea
b = chartb.plots._plotArea
k = a.getchildren()[0]
b.insert(1, k)
prs.save("test.pptx")
# Result: "it shows it needed repairing"
The reporter confirmed: "when I open test.pptx, it shows it needed repairing." Other commenters in the issue thread reported the same result with variations of this approach.
Why does the XML workaround trigger repair?
The XML splicing approach fails for three technical reasons, all related to how OOXML charts store their data:
- Orphaned data references. Each chart in a PPTX file has an embedded Excel workbook (stored as a
.xlsxfile inside the PPTX zip) that contains the chart data. When you splice a<c:lineChart>element from chart2 into chart1's plot area, the line chart element still references data cells in chart2's embedded workbook — which does not exist in chart1's context. PowerPoint detects this as a data integrity violation. - Axis ID conflicts. Each chart element references category and value axes by ID. The spliced line chart references axis IDs that were valid in chart2 but may not exist (or may conflict with existing IDs) in chart1. PowerPoint expects all axis references within a plot area to resolve to valid axis definitions.
- Relationship ID mismatches. As documented in our repair dialog guide, PPTX files use relationship IDs to link internal components. The spliced element carries relationships from the source chart that are invalid in the destination context.
According to the python-pptx plot analysis documentation, a correct combo chart requires all series to share a single data source and a single set of axis definitions, with individual series assigned to different chart type elements within one plot area. Reconstructing this from two separate charts programmatically — while maintaining valid data references, axis IDs, and relationship IDs — is equivalent to implementing the combo chart feature itself.
Alternative 1: PptxGenJS (JavaScript, free)
According to PptxGenJS's chart documentation, "charts of almost any type can be added to slides, including combo and 3D charts." Combo charts are a first-class feature — no workarounds, no XML manipulation, no repair dialogs.
import pptxgen from "pptxgenjs";
const pres = new pptxgen();
const slide = pres.addSlide();
slide.addChart(
pres.ChartType.bar,
[
{
name: "Revenue",
labels: ["Q1", "Q2", "Q3", "Q4"],
values: [1200, 1800, 2400, 3100]
},
{
name: "Growth %",
labels: ["Q1", "Q2", "Q3", "Q4"],
values: [8, 12, 15, 22]
}
],
{
x: 1, y: 1, w: 8, h: 4.5,
showLegend: true,
catAxisOrientation: "minMax",
secondaryValAxis: true,
secondaryCatAxis: false,
// second series rendered as line on secondary axis
chartColorsOpacity: 80
}
);
await pres.writeFile({ fileName: "combo-chart.pptx" });
PptxGenJS has ~5.8K GitHub stars, ~2.7M weekly npm downloads (as of August 2026), and is MIT-licensed. The trade-off: it is JavaScript, not Python. If your stack is Python-only, PptxGenJS requires adding a Node.js execution layer — either as a microservice, a subprocess call, or a serverless function.
Alternative 2: Runstamp (JSON, multi-format)
Runstamp supports extended PPTX chart features through Pro ($199/mo), which unlocks Pro features across PPTX, PDF, DOCX, and XLSX. The same JSON definition can also drive all four free lite engines.
import { generate } from "@runstamp/pptx";
const doc = {
slides: [{
elements: [{
type: "chart",
chartType: "combo",
data: {
categories: ["Q1", "Q2", "Q3", "Q4"],
series: [
{
name: "Revenue",
type: "bar",
values: [1200, 1800, 2400, 3100]
},
{
name: "Growth %",
type: "line",
axis: "secondary",
values: [8, 12, 15, 22]
}
]
}
}]
}]
};
const buffer = await generate(doc);
writeFileSync("combo-chart.pptx", buffer);
Runstamp's advantage over PptxGenJS for combo charts is the data model: each series explicitly declares its type ("bar", "line") and axis ("primary", "secondary"). This is clearer than PptxGenJS's approach of using chart options and series ordering to determine which series becomes the secondary chart type.
Both produce valid OOXML combo charts that open in PowerPoint without repair dialogs. Both generate native editable charts — recipients can click the chart and modify the data. For a detailed comparison of all three tools, see the full python-pptx vs PptxGenJS vs Runstamp comparison.
Which chart types does each tool support?
| Chart type | python-pptx | PptxGenJS | Runstamp |
|---|---|---|---|
| Bar / column | Yes | Yes | Yes |
| Line | Yes | Yes | Yes |
| Pie | Yes | Yes | Yes |
| Scatter / XY | Yes | Yes | Yes |
| Area | Yes | Yes | Yes |
| Doughnut | Yes | Yes | Planned |
| Radar | Yes | Yes | Planned |
| Bubble | Yes | Yes | Planned |
| Combo (bar + line) | No (8 years) | Yes | Yes (Pro) |
| 3D charts | No | Yes | Planned |
| Waterfall | No | No | Planned |
| Treemap / sunburst | No | No | Planned |
| Secondary Y-axis | No | Yes | Yes |
| Editable by recipient | Yes | Yes | Yes |
Keep the document workflow in your product.
Use the catalog to select a native action, prove it against a real artifact, then embed review and release evidence where your customer already works.

