DEV Community

Rehan Sanjay Venkatesan
Rehan Sanjay Venkatesan

Posted on

I installed all 67 Pipecat community plugins. 19 of them don't work.

Pipecat's docs list every community-maintained plugin on one page: speech-to-text, text-to-speech, serializers, VAD, analytics, transports. Pick a vendor, pip install, follow the snippet.

For 19 of the 67 listed this morning, that doesn't work. The install fails, or the import raises, or the package pins a version of Pipecat from a year ago and quietly drags your whole environment backwards.

Nobody is hiding this. It's just that nothing was checking.

What the checker does

Every night, for each plugin on the supported-services page:

  1. Create a fresh environment.
  2. Install the plugin together with the latest pipecat-ai release on PyPI — what a new user gets today.
  3. Do it again against pipecat-ai main — what users get in the next release.
  4. Import the plugin and construct every Pipecat service class it defines.

No API keys are used anywhere, so this doesn't test whether a provider's servers answer. It tests the part that breaks far more often: install, import, and API shape — renamed or removed Pipecat symbols, new abstract methods, version pins.

The plugin list is re-read from the docs on every run, so new plugins are picked up automatically. That matters: the page had 56 entries three weeks ago and has 67 today.

Results from this morning's run, against pipecat-ai 1.12.0:

Status Count
Works 48
Partial 1
Broken on import 10
Pinned to an older Pipecat 5
Can't be installed at all 3

The failures cluster, and that's the interesting part

Six plugins die on the same line. In Pipecat 1.8 the settings sentinel _NotGiven was removed from pipecat.services.settings — it's NotGiven in pipecat.utils.types now. Six plugins still import the old name, so they raise before doing anything else:

ImportError: cannot import name '_NotGiven' from 'pipecat.services.settings'
Enter fullscreen mode Exit fullscreen mode

Six different companies, six separate repositories, one upstream rename. Each fix is a single import line.

Three more broke on renamed frames. One imports StartInterruptionFrame, another LLMMessagesFrame, another assert_given — none of which exist under those names any more.

Two are missing their own dependency. The plugin expects a vendor SDK that isn't installable from where it looks for it.

Five pin Pipecat backwards. One requires pipecat-ai>=0.0.97,<0.1.0. Another wants ==0.0.105. A third pins ==1.8.1 exactly. These don't error — which is worse. Your resolver silently downgrades Pipecat, or refuses to solve, and the reason isn't obvious from the traceback you eventually get.

Three can't be installed at all. Two checkouts have neither a pyproject.toml nor a setup.py, so there's nothing to install. One isn't on PyPI under the name the docs use.

This is not upstream's fault

It would be easy to read the _NotGiven cluster as Pipecat breaking its ecosystem. It isn't.

The 1.8.0 changelog flagged the change with a ⚠️ and spelled out the migration:

NOT_GIVEN, NotGiven, is_given() and assert_given() now come from pipecat.utils.types and are no longer importable from pipecat.services.settings. The private pipecat.services.settings._NotGiven is now the public pipecat.utils.types.NotGiven.

They announced it, named the destination, and were right that the old name was private — plugin authors were importing an underscore-prefixed symbol out of someone else's module, which was always going to be fragile.

The supported-services page is equally clear about ownership: community plugins are distributed separately and issues go to their own repositories.

So upstream did the things you'd ask for. The breakage happened anyway.

Why it keeps happening

Community plugins live in their own repositories, usually maintained by one engineer at the vendor. Pipecat ships often and moves fast, which is a virtue. But a plugin repo has no CI that installs against Pipecat main, so an upstream rename doesn't produce a red build anywhere — it produces a broken install six weeks later, for a user who has no relationship with either party and will simply choose a different vendor.

That's the gap: the breakage is public, deterministic and cheap to detect, and there was no place where anyone looked at all of them at once.

It measures recovery too

Between two runs in September, one plugin loosened its pin and moved from "needs older Pipecat" to "works". That's the part I didn't expect to enjoy: the table isn't a wall of shame, it's a live picture. Anything on it can move, and most of these are one line away from moving.

Running against main as well as the current release is deliberate, and it's the part I'd most like vendors to use. A plugin that breaks against main today has until the next Pipecat release to fix it, rather than finding out from an issue after the fact.

If you're choosing a plugin

  • Check the pin before you check the features. Four of the five pinned plugins will silently downgrade Pipecat in your environment rather than fail, and you'll debug it as something else entirely.
  • An import test is a five-second question. python -c "import pipecat_<name>" in a fresh environment tells you more than a README.
  • Check when the repo last moved. All ten broken plugins have been broken since late August. That's visible from the outside before you commit.

I've been sending the fixes

Rather than just publishing a list, I opened PRs for the ones I could fix from outside — eleven of them across eleven repositories.

What happened next is its own post, and it isn't the happy ending: three weeks on, none of them have merged. I'll write that one up separately, because the result surprised me more than the original findings did.

The repo, with the full table and the nightly results:
https://github.com/Rehansanjay/pipecat-plugin-health

If your plugin is on the broken list and you'd rather have a PR than a bug report, say so and I'll send one.

Top comments (3)

Collapse
 
rehansanjay profile image
Rehan Sanjay Venkatesan •

Follow-up to this, and your point about one word doing more than one job came back around in a way I did not expect.

One of the rows I called broken was a plugin whose maintainer had already fixed it on main — PyPI was just still serving an older wheel. The checker installs from PyPI, so it was answering "are your users getting something that works" while printing a label that read "your code is broken". Those are different sentences and they ask the maintainer for different things: a patch, or a release.

It now builds from the plugin's own source when the release fails, so a row can say "fixed, not released". I went in expecting a pile of those and found exactly one.

dev.to/rehansanjay/my-tool-called-...

Collapse
 
arhancanli profile image
Arhan Canli •

The table adds up (48 + 1 + 10 + 5 + 3 = 67), and so does the headline: 19 is the 10 import failures, the 5 pins, the 3 uninstallable ones and the 1 partial. Worth knowing when quoting it, because the pinned five are the only group where nothing has been shown to be broken. A plugin pinned to ==1.8.1 fails to resolve, but it may run fine on 1.12.

That is checkable with the machinery you already have: install the five with --no-deps next to the current pipecat-ai, then run the same import and construct step. Any that pass are "pin too tight" (a one-line fix for the maintainer). Any that fail are real API breaks hiding behind the pin. Those are two different messages to send a vendor, and the single "pinned to an older Pipecat" row merges them.

One more thing for the table: record the pipecat-ai version next to each run. "Works against 1.12.0" is a dated claim, and with a nightly run the recovery you mention (a plugin loosening its pin) is only readable if the version is in the row.

Where do the three uninstallable ones sit in the count, as failures or as a separate class from the 16 that were actually tried?

Collapse
 
rehansanjay profile image
Rehan Sanjay Venkatesan •

You were right, and I ran it. All five come back compatible against 1.12.0:

Deepdub   pinned >=0.0.97,<0.1.0   ->  compatible
Maya      pinned ==1.8.1           ->  compatible
Memcode   pinned >=1.10,<1.11      ->  compatible
Oruk      pinned >=1.8.1,<1.9      ->  compatible
Wavix     pinned ==0.0.105         ->  compatible
Enter fullscreen mode Exit fullscreen mode

Every one imports and constructs. Zero incompatible. So that row was five one-line edits to a version range, not five broken plugins, and my headline was counting them as the latter.

One adjustment to the method, which is worth passing back. --no-deps skips the plugin's own SDK as well as pipecat, so on the first run Deepdub and Memcode came back unknown — No module named 'deepdub' and No module named 'memcode_sdk', neither of which has anything to do with pipecat. uv's --overrides is sharper: the plugin resolves with all its real dependencies and only pipecat-ai is forced to the version under test. That turned both unknowns into answers. I've kept --no-deps as a fallback for when an override can't resolve, and anything failing for a reason the checker doesn't recognise stays unknown rather than getting guessed at.

On your question about the three uninstallable ones — they're a separate class, and you're right that the count shouldn't have merged them either. They were never probed at all: nothing installed, so the import-and-construct step never ran on them.

Going to look at all three today turned up something I didn't expect, which is that "uninstallable" was hiding real breakage. Hakim has no pyproject.toml, so the pip install git+... in its own README can't build anything — and once I made it installable it turned out to also fail on the _NotGiven import, the same break as six others. It had been invisible because nobody could get far enough to hit it. Uplift AI was the same shape plus an undeclared httpx. Simplismart isn't on PyPI under the name the docs give, so that one really is just absent.

Your comment also made me look at the categories properly, and found a third one that was wrong without you asking. Hecttor imports hecttor_sdk, which its own README says isn't published — you contact them for a wheel and an API key. That plugin isn't broken, it works for their customers, and no checker standing outside the licence can ever see it pass. It has its own status now. The checker asks PyPI whether the missing module exists and only gates on a definite no, because reading "couldn't ask" as "doesn't exist" would quietly turn real breakage into licensing whenever it ran offline.

Net of all that, the defensible number is 13 of 67 rather than the 18 of 56 I published. Six plugins I called broken were working the whole time.

And the version is recorded against the pin verdict now, so "works against 1.12.0" is dated rather than floating — your second point.

I've written the whole thing up as a follow-up and credited you in it: dev.to/rehansanjay/i-sent-the-fixe...

Thanks for actually checking the arithmetic. That's the most useful thing anyone's done with this.