NginxProxyManager/nginx-proxy-manager · error
Plugin not found
Error message
Plugin not found
What it means
This is an HTTP 404 from POST /api/nginx/certificates/:plugin (the certbot DNS plugin install endpoint) in backend/routes/ci.js. It compares req.params.plugin against the dnsPlugins registry; an unknown or misspelled name yields a 404 with error "Plugin not found" instead of attempting a package install.
Source
Thrown at backend/routes/ci.js:45
res.status(200).send(dnsPlugins);
});
/**
* /api/ci/certbot-plugins/{plugin}
*/
router
.route("/certbot-plugins/:plugin")
.options((_, res) => {
res.sendStatus(204);
})
// Install a certbot plugin
.post(async (req, res, next) => {
try {
const pluginName = req.params.plugin;
// check if plugin exists
if (!dnsPlugins[pluginName]) {
return res.status(404).send({
error: "Plugin not found",
});
}
await installPlugin(pluginName);
res.status(200).send(true);
} catch (err) {
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
return;
});
export default router;
View on GitHub (pinned to 934a3fafe5)
Solutions
- Check the exact key in backend/internal/dns-plugins.js (the dnsPlugins object) and use that slug verbatim, e.g. 'cloudflare', 'digitalocean', 'google'
- List available plugins first (the GET side of the same route / docs) and pick the key, not the display name
- If the provider genuinely is missing, upgrade NPM to a version that includes the dns-plugins entry, or add a custom entry locally and rebuild
Example fix
# before curl -X POST https://npm.example.com/api/nginx/certificates/Cloudflare # after curl -X POST https://npm.example.com/api/nginx/certificates/cloudflare
Defensive patterns
Strategy: validation
Validate before calling
import dnsPlugins from './internal/dns-plugins.js';
const pluginExists = (name: string) => Object.prototype.hasOwnProperty.call(dnsPlugins, name);
if (!pluginExists(pluginName)) {
throw new Error(`Unsupported plugin '${pluginName}'. Available: ${Object.keys(dnsPlugins).join(', ')}`);
} Type guard
const isKnownDnsPlugin = (name: string): name is keyof typeof dnsPlugins => Object.prototype.hasOwnProperty.call(dnsPlugins, name);
Try / catch
const res = await fetch(`/api/nginx/certificates/${plugin}`);
if (res.status === 404) {
// re-fetch the plugin list and reconcile the slug before retrying
} Prevention
- Drive plugin selection from the server-provided list (GET endpoint) so slugs always match the deployed version
- Pin API scripts to a known NPM version and re-verify plugin keys after upgrades
- Add integration tests asserting every slug offered by the UI exists in dnsPlugins
When it happens
Trigger: POSTing to the endpoint with a plugin slug that is not a key of the dnsPlugins map — e.g. 'cloudflaree', 'route53' when unsupported, or passing a display name like 'Cloudflare DNS' instead of the slug 'cloudflare'.
Common situations: Typos in the plugin slug from hand-crafted curl/API scripts, UI dropdowns sending labels instead of keys, or scripts written against an older/newer version of NPM whose dnsPlugins registry differs (provider added/removed in that version).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Unknown DNS provider '${certificate.meta.dns_provider}'
- Database config does not exist! Please read the instructions
- No 2FA challenge pending
- No files were uploaded
AI-assisted analysis of NginxProxyManager/nginx-proxy-manager@934a3fafe5 (2026-08-27).
Data as JSON: /api/errors/f41713291b7d71ef.
Report an issue: GitHub.