Mintplex-Labs/anything-llm · error · Error
AGENT_KEENABLE_API_URL must use https:// (or target a…
Error message
AGENT_KEENABLE_API_URL must use https:// (or target a loopback host).
What it means
The Keenable web-search plugin validates the optional AGENT_KEENABLE_API_URL override before using it as the search endpoint. It parses the URL and rejects any value whose protocol is not https: unless the hostname is a loopback host (localhost, 127.0.0.1, ::1, host.docker.internal). This guard prevents API keys and search queries from being sent over plaintext http to non-local hosts, where they could be intercepted.
Solutions
- Change AGENT_KEENABLE_API_URL to use https:// (terminate TLS on the endpoint or front it with a reverse proxy like nginx/Caddy with a certificate).
- If the endpoint is genuinely local, use one of the whitelisted hostnames: http://localhost:PORT, http://127.0.0.1:PORT, http://[::1]:PORT, or http://host.docker.internal:PORT (when running in Docker).
- If the env var is not needed, unset AGENT_KEENABLE_API_URL entirely — the plugin then defaults to the built-in https://api.keenable.ai.
- For a plain-http endpoint on another address, extend the loopback whitelist in server/utils/agents/aibitat/plugins/web-browsing.js (lines 1358-1363) — only do this on trusted networks.
Example fix
// before AGENT_KEENABLE_API_URL=http://api.keenable.ai // after AGENT_KEENABLE_API_URL=https://api.keenable.ai
Defensive patterns
Strategy: validation
Validate before calling
function isKeenableUrlSafe(raw) {
try {
const u = new URL(raw);
const loopback = ["localhost", "127.0.0.1", "::1", "host.docker.internal"];
return u.protocol === "https:" || loopback.includes(u.hostname);
} catch {
return false;
}
}
// before starting the agent:
if (process.env.AGENT_KEENABLE_API_URL && !isKeenableUrlSafe(process.env.AGENT_KEENABLE_API_URL))
throw new Error("AGENT_KEENABLE_API_URL must use https:// (or target a loopback host)."); Type guard
function isHttpsOrLoopback(url) {
if (!(url instanceof URL)) return false;
const loopback = ["localhost", "127.0.0.1", "::1", "host.docker.internal"];
return url.protocol === "https:" || loopback.includes(url.hostname);
} Try / catch
try {
await agent._keenableSearch(query);
} catch (e) {
if (e.message.includes("must use https://")) {
console.error("Fix AGENT_KEENABLE_API_URL:", e.message);
} else {
throw e;
}
} Prevention
- Always configure https:// endpoints for non-local Keenable proxies.
- Only use http:// with the literal loopback hostnames (localhost, 127.0.0.1, ::1, host.docker.internal).
- Validate the env var at deployment/startup with a URL parse instead of discovering the error at first search.
- Prefer omitting AGENT_KEENABLE_API_URL unless you actually self-host the API.
- Terminate TLS with a reverse proxy (Caddy/nginx) rather than relaxing the whitelist.
When it happens
Trigger: AGENT_KEENABLE_API_URL is set to an http:// URL (e.g. http://api.keenable.ai or http://my-proxy.example.com) whose hostname is not one of the four whitelisted loopback names; the error is thrown inside setup when the plugin parses the env var at line 1357-1369.
Common situations: Developers self-hosting a Keenable proxy behind plain http on a remote VM, typos like 'http://' copied from docs, pointing at an internal IP (e.g. http://10.0.0.5:8080) that is not in the loopback whitelist, or forgetting a reverse-proxy TLS termination step. Note even 0.0.0.0 or a LAN hostname fails because only the four literal names pass.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Access denied - symlink target outside allowed directories.
- Cannot copy symbolic link
- [EmbedConfig] Embed was created with no allowed-domains…
- GenericOpenAI must have a valid base path to use for the…
- Invalid COLLECTOR_PORT
AI-assisted analysis of Mintplex-Labs/anything-llm@a145d4d87d (2026-09-15).
Data as JSON: /api/errors/279e35b8579ee878.
Report an issue: GitHub.
Appendix: source
Thrown at server/utils/agents/aibitat/plugins/web-browsing.js:1367
return result;
},
_keenableSearch: async function (query) {
const apiKey = (process.env.AGENT_KEENABLE_API_KEY || "").trim();
let baseUrl = "https://api.keenable.ai";
if (process.env.AGENT_KEENABLE_API_URL) {
try {
const parsed = new URL(process.env.AGENT_KEENABLE_API_URL);
const isLoopback = [
"localhost",
"127.0.0.1",
"::1",
"host.docker.internal",
].includes(parsed.hostname);
if (parsed.protocol === "https:" || isLoopback)
baseUrl = parsed.origin;
else
throw new Error(
"AGENT_KEENABLE_API_URL must use https:// (or target a loopback host)."
);
} catch (e) {
this.super.handlerProps.log(
`invalid Keenable Search URL: ${e.message}`
);
return `Keenable search is misconfigured: ${e.message}`;
}
}
this.super.introspect(
`${this.caller}: Using Keenable to search for "${
query.length > 100 ? `${query.slice(0, 100)}...` : query
}"`
);
const headers = {
"Content-Type": "application/json",View on GitHub (pinned to a145d4d87d)