Has code that worked for years suddenly stopped resampling by month?
df.set_index('when').resample('M').sum()
ValueError: Invalid frequency: M. Failed to parse with error message: ValueError("'M' is no longer supported for offsets. Please use 'ME' instead.")
The fix is in the message: use ME.
df.set_index('when').resample('ME').sum()
n
when
2026-01-31 1
2026-02-28 2
2026-03-31 3
What makes this one worth a page is not the fix, which is a two-character edit. It is that nearly every Pandas tutorial, Stack Overflow answer and notebook written before 2024 uses 'M', so the error arrives at people who copied something that was correct when it was written.
ME means month end
The rename exists because 'M' was ambiguous. It meant month end, but it reads like month, and there was already a separate alias for month start. Spelling out which end you want removes the guess:
pd.date_range('2026-01-01', periods=3, freq='ME')
DatetimeIndex(['2026-01-31', '2026-02-28', '2026-03-31'], dtype='datetime64[us]', freq='ME')
Every timestamp lands on the last day of its month. If you wanted the first day, that is MS, which did not change.
The other renamed aliases
M is the one people meet first, because monthly resampling is so common, but it travels with a family. The old spellings all raise the same shape of error:
| Old | New | Means |
|---|---|---|
M |
ME |
month end |
Q |
QE |
quarter end |
Y or A |
YE |
year end |
H |
h |
hourly |
T or min |
min |
minutes |
S |
s |
seconds |
L or ms |
ms |
milliseconds |
BM |
BME |
business month end |
BQ |
BQE |
business quarter end |
The lowercase ones are the second surprise. 'H' became 'h', so a frequency string that differs only in case is now the difference between working code and an exception.
The start-of-period aliases were left alone: MS, QS, YS, BMS all still mean what they meant.
It is not only resample
The same parser sits behind several methods, so the error follows you around:
pd.date_range('2026-01-01', periods=3, freq='M')
ValueError: Invalid frequency: M. Failed to parse with error message: ValueError("'M' is no longer supported for offsets. Please use 'ME' instead.")
df.groupby(pd.Grouper(key='when', freq='M')).sum()
ValueError: Invalid frequency: M. Failed to parse with error message: ValueError("'M' is no longer supported for offsets. Please use 'ME' instead.")
period_range, asfreq and rolling with a time-based window all go through it too. If you are upgrading an older project, searching for the quoted strings is faster than waiting to meet each one.
The mistakes I see most often
Fixing it in one place. A notebook usually resamples more than once, and the error only reports the line Python reached first.
Reading ME as "month, English". It is month end. Pair it with MS for month start in your head and it stops looking cryptic.
Assuming the data is wrong. Nothing about the frame changed. This is a vocabulary change in Pandas, and the values, index and dtypes are all exactly as they were.
Practice
See resample in Pandas for reshaping a time series, and Grouper in Pandas for grouping by period inside a groupby.