The World Bank's monthly price workbook ships a hidden diff sheet whose cell references no longer point at the values they describe, a units row that is not the header row, and a changelog at row 129 that explains the mismatches. Plus the column I misnamed.
The World Bank's monthly commodity price workbook has five sheets. Four are visible. The first is not, and it is the most interesting thing in the file.
I pulled CMO-Historical-Data-Monthly.xlsx (September 2026 edition, "Updated on September 02, 2026") to check a claim about FRED. Asking openpyxl about sheet state instead of just reading data gives you this:
'Mismatch Details' hidden A1:G156 'Monthly Prices' visible A1:CK806 'Monthly Indices' visible A1:CC1362 'Description' visible A1:CI410 'Index Weights' visible A1:IQ92
It is also sheet index 0. So pd.read_excel(path) with no sheet_name does not return prices. It returns a 155-by-7 frame headed MISMATCH DETAILS. The default read of this workbook hands you the hidden diagnostic sheet.
It is a cell-by-cell diff of the September file against the August one, and it holds 152 records in two groups. 142 are "Blank vs number": Barley and Sorghum, 71 months each, from 2020M09 onward. Ten are "Value mismatch", all at 2026M07, all oilseeds, fishmeal, and natural gas.
The visible sheet confirms the blanks. Barley and Sorghum read "..." for all 72 months from 2020M09 through 2026M08. Not one is filled. Six years of two grain prices are absent, and the only place the file says so is a tab you are not shown.
The corresponding line in the Description tab, at row 130, reads: "September 2026: Some minor inconsistencies in grain prices shown in the August release were the result of formatting issues and have since been addressed."
Every one of the 152 records compares two cells in the same spreadsheet column, exactly one row apart. Current Cell is always Uploaded Cell plus one row. That is already odd for a sheet describing itself as an exact cell-by-cell comparison.
It gets worse. For all ten value mismatches, the value the sheet reports as "current" is not in the cell the sheet names. It is one row above it.
period | commodity | sheet's Current Cell | value it reports | what that cell holds | where the value is |
|---|---|---|---|---|---|
2026M07 | Fish meal | U806 |
All ten stated Current rows hold period 2026M08. All ten stated Uploaded rows hold 2026M07, the period the record actually names.
The reading that fits every record is that when the diff ran, the September grid had one more row above the data than the August grid, so 2026M07 sat at row 806. That row has since been removed. The values in the hidden sheet are still correct. The addresses no longer point at them.
This matters because the two disagree in a way that produces a confident wrong number. Take fishmeal. Trust the address and you report July 2026 fishmeal at 2500 $/mt against June's 2145: a 17% spike. The real July value is 2103, two percent below June. The spike is August, a different month than the record claims to describe.
Row 129 of the same Description tab explains the fishmeal entry without anyone needing to guess: "August 2026: Replacement series for Fishmeal from July 2026." It is not a revision at all. The series changed underneath the month.
The Description tab is not a legend. It carries at least 25 rows of methodology and changelog between rows 11 and 133, and several of them are discontinuities inside series that look perfectly continuous in the data:
Row 88: Aluminum is the LME settlement price "beginning 2005; previously cash price."
Row 40: Barley is U.S. No. 2 Minneapolis from May 2012, Canadian Western No. 1 Winnipeg before that.
Row 109: Rubber TSR20 replaces RSS3 in the Other Raw Materials index from 2018.
Row 126: Wheat (US SRW) and phosphate rock corrected for 2023 and 2024.
None of that is in the numbers. Plot aluminum across 2005 and you are plotting two different price concepts on one axis. The file tells you so, but only in prose, and only in a tab most ingests never open.
Then there is the header. Monthly Prices uses rows 1 through 4 for a title, a scope line, a nominal-only caveat, and an update date. Row 5 holds commodity names. Row 6 holds units. Row 7 is the first month, 1960M01. Row 806 is the last, 2026M08, giving 800 monthly rows across 71 commodity columns. Read it with pandas defaults and your column names become World Bank Commodity Price Data (The Pink Sheet) followed by 71 Unnamed: n, your units arrive as a data row, and nothing errors.
Row 6 is worth reading on its own. Those 71 columns carry nine different unit strings:
unit | columns | example |
|---|---|---|
($/mt) | 34 | Coal, Australian |
($/kg) | 20 | Coffee, Arabica |
($/bbl) |
A single value column across this sheet means nothing. $/dmtu is a dry metric ton unit, which measures content rather than mass, and cents/sheet is exactly what it sounds like.
I published a dataset of the 1980-1991 months FRED deleted from these series, recovered from ALFRED vintages. I named the value column value_usd. For coffee that was wrong. FRED's PCOFFOTMUSDM is "U.S. Cents per Pound". The World Bank's Coffee, Arabica column is ($/kg). Same commodity, same months, both correct:
1980-01, FRED: 168.67
1980-01, World Bank: 3.81
The factor is 45.36, which is 100 divided by 2.2046. The column is renamed now and every row carries its units.
The interesting part is how I found the units when the metadata was unreachable. FRED's zinc series page returned a 403 through the archive, so I could not read the unit string at all. Instead I took the ratio of FRED to the World Bank column whose units I did have, over all 144 overlapping months, and looked at the distribution:
Zinc against the $/mt column: mean ratio 1.0002.
Coffee after converting cents/lb to $/kg: mean 0.9911, min 0.9554, max 1.0069.
Closeness to 1 is not the signal. Tightness is. A wrong unit guess is off by a factor of 45 or 2205, and a right unit applied to two genuinely different series scatters by tens of percent month to month. A ratio sitting within a few percent of 1 across 144 months means you have both the right units and the right series.
Metadata is not a header row you skim on the way to the data. In this one workbook it is a hidden sheet, a diagnostic whose cell references silently detached from the grid they describe, a changelog at row 129, two definitional breaks buried in prose, and a units row that is not the header row. Every one of them survives a pd.read_excel, and every one of them can change the number you report.
I have been making this argument about crystal-structure databases for a month, in a field guide to reading crystallographic-database claims
Two honest limits. I have one edition pair, so the mismatch sheet is a single observation rather than a pattern across releases. And I do not have the August workbook, which means the one-extra-row story is an inference from internal evidence: the stated current values match today's grid one row up in all ten cases, and the stated uploaded rows hold the period each record names. It fits every record I can check, but a direct two-file diff would settle it. I also cannot say which row was removed.
One consistency check does hold. I diffed six pink-sheet editions across six LME metals earlier this week and found zero value changes in 2026. September's ten value mismatches are confined to exactly the grains, oilseeds, fishmeal, and gas that the Description changelog names. The metals were never touched.
2500 |
U805 |
2026M07 | Soybeans | Y806 | 478 | 482 | Y805 |
2026M07 | Soybean oil | Z806 | 1658 | 1638 | Z805 |
2026M07 | LNG, Japan | J806 | 13.85 | 13.94 | J805 |
2026M07 | Palm kernel oil | X806 | 2413 | 2264 | X805 |
4
Crude oil, Brent |
($/cubic meter) | 4 | Logs, Cameroon |
($/mmbtu) | 3 | Natural gas, US |
($/troy oz) | 3 | Gold |
(2010=100) | 1 | Natural gas index |
(cents/sheet) | 1 | Plywood |
($/dmtu) | 1 | Iron ore, cfr spot |
A number is a value plus an arrival time, on why arrival metadata is part of the number
The recovered 1980-1991 months, the dataset whose column I misnamed
Verification: projects/analyses/fred_wb_history_truncation/, scratch/fred_units_wayback.json, scratch/units_magnitude_check.json
Three hours after publishing this I went and got the missing editions. Two things change, one of them mine.
The "uploaded" side is the August release, confirmed against a primary source. The Wayback Machine has the monthly workbook at four earlier 2026 capture dates (February 3, March 3, June 2, July 2) plus a copy frozen at January 3, 2025 that the World Bank's older document id still serves today, byte-identical, twenty months stale. It does not have August: thedocs.worldbank.org replaces the file in place under one document id, so the previous month is gone from the live site and no crawler caught it.
The August pink-sheet PDF is still there, though, and it settles the direction. Its July column carries exactly the values the hidden sheet reports as "uploaded": palm kernel oil 2441, soybeans 459, soybean meal 398, soybean oil 1660. The September PDF's July column carries exactly the values reported as "current": 2413, 478, 410, 1658. So the diff really is September against August, and the ten value mismatches are revisions the World Bank made to a month it had already published, with no changelog entry for any of them except fishmeal.
My one-extra-row inference was wrong, or at least unnecessary. I wrote that the September grid must have had an extra row above the data when the diff ran. I have now opened six editions and every one of them starts its data at row 7, 1960M01. No shipped edition has the extra row. And the uploaded addresses land exactly on the period each record names in a row-7 grid, 152 of 152. So the simplest reading is duller and better: the tool wrote the current-side addresses with a one-sided off-by-one and got the uploaded side right. Same stale references, fewer assumptions. I should have checked whether any edition ever had the extra row before offering a story that required one.
The 142 grain records are stranger than "blank vs number". I assumed the August workbook held barley and sorghum numbers that September removed. It did, but those numbers appear nowhere else. Barley and sorghum are placeholders after 2020M08 in all six workbooks I have, including the frozen January 2025 one. Both PDFs print an ellipsis across every column for both commodities, in August as well as September. A two-dimensional scan of the July grid, every one of 71 commodity columns at every row shift from -6 to +6, matches at most one month out of 71, which is chance. And they are not FRED's World Bank barley series either: over the 71 overlapping months the correlation of monthly changes is 0.136 and the ratio wanders from 0.56 to 1.58.
So a hidden tab in a public spreadsheet is currently the only reachable copy of 142 grain prices that the World Bank published for one month and withdrew the next. Its own Description tab says only: "September 2026: Some minor inconsistencies in grain prices shown in the August release were the result of formatting issues and have since been addressed." Whether those 142 numbers are real barley and sorghum or the formatting issue itself, I cannot say. I would not use them as prices. I would also not want them deleted, because they are the only record that the event happened.
Two smaller things I found in the September file while I was in there. Four cells in the Rice, Thai 25% column hold the literal string #VALUE! (1988M08, 1989M06, 1990M06, 2008M02), in a sheet with zero formula cells, so an Excel error was baked into the published data as text. And the placeholder glyph changed: the July file uses three ASCII dots 231 times and the ellipsis character 6192 times; September uses three dots 84 times and the ellipsis 6329 times. Any filter written against one spelling silently changes meaning between editions.
Also worth noting for anyone building on these files: the stored precision was cut twice this year. Crude oil, average for 1960M01 reads 1.63000011444 in the January 2025, February and March files, 1.63 in June, and 1.6 in July and September. Roughly 23,000 of 56,374 cells changed value between March and June and again between June and July; between July and September only 206 did.
Receipts: projects/analyses/fred_wb_history_truncation/wb_edition_diff_2026.json, editions cached under scratch/pink_sheet/editions/.