JuliusBrussee/caveman · error · MiddlewareError
remote_content_not_enabled
remote_content_not_enabled
Error message
remote_content_not_enabled
What it means
For non-local endpoints, the Runtime constructor requires explicit opt-in to loading remote content: the endpoint hostname must be localhost/127.0.0.1/[::1] to be exempt, otherwise options.allowRemoteContent must be true AND the protocol must be https:. If either condition fails, MiddlewareError with code 'remote_content_not_enabled' is thrown to prevent silently sending code/content over plaintext or to unexpected remote hosts.
Solutions
- Add allowRemoteContent: true to the Runtime options when targeting a remote host
- Ensure the remote endpoint uses https:// (never http:// for non-local hosts)
- Keep using http://127.0.0.1:8787 (or localhost/[::1]) for local development where the flag is unnecessary
- If this fires in CI, check which environment's endpoint the config resolved to
Example fix
// before
const rt = new Runtime({ endpoint: 'https://cave.example.com' });
// after
const rt = new Runtime({ endpoint: 'https://cave.example.com', allowRemoteContent: true }); Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(endpoint);
const local = ['127.0.0.1', '[::1]', 'localhost'].includes(u.hostname);
if (!local && !(allowRemoteContent && u.protocol === 'https:')) throw new Error('remote endpoints require https and allowRemoteContent: true'); Type guard
function remoteEndpointAllowed(endpoint: string, allowRemoteContent: boolean): boolean { const u = new URL(endpoint); const local = ['127.0.0.1', '[::1]', 'localhost'].includes(u.hostname); return local || (allowRemoteContent && u.protocol === 'https:'); } Try / catch
try { const rt = new Runtime({ endpoint, allowRemoteContent }); } catch (e) { if (e instanceof MiddlewareError && e.code === 'remote_content_not_enabled') throw new Error('set allowRemoteContent: true and use https:// for remote middleware endpoints'); throw e; } Prevention
- Set allowRemoteContent: true explicitly whenever the endpoint host is not localhost
- Always use https:// for remote endpoints; treat http:// remote as a config bug
- Keep local dev on http://127.0.0.1:8787 to avoid needing the flag
- Audit CI config so staging/prod endpoints include the opt-in flag
When it happens
Trigger: new Runtime({ endpoint: 'https://cave.example.com' }) without allowRemoteContent: true; or new Runtime({ endpoint: 'http://cave.example.com', allowRemoteContent: true }) — plain-text http to a remote host is always rejected regardless of the flag. Only https + explicit opt-in passes for remote endpoints.
Common situations: Deploying the middleware against a staging/production Cave host while keeping local-dev options; configuring http:// for a remote VM during testing; forgetting the allowRemoteContent flag when the endpoint moved from localhost to a remote host in CI.
Related errors
- remote_content_not_enabled
- auth_token: in is ignored — the inbound token is read only…
- caveman build: config must use strict lock and required…
- compat upstream forward_headers
- --instance requires a private HTTPS origin (HTTP loopback…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a676623fe2522f9e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/typescript/src/middleware/runtime.ts:96
private readonly options: RuntimeOptions;
private readonly fetcher: typeof globalThis.fetch;
private readonly lifetime = new AbortController();
private readonly bindings = new WeakSet<RecoveryBinding>();
private readonly capsCache: { value: Capabilities | null } = { value: null };
private failures = 0;
private openUntil = 0;
private pending = 0;
private receiptsPending = 0;
private fetchesPending = 0;
private receiptFetchesPending = 0;
private receiptTail: Promise<void> = Promise.resolve();
private reported: CallReport | null = null;
constructor(options: RuntimeOptions = {}) {
const url = new URL(options.endpoint ?? 'http://127.0.0.1:8787');
const local = ['127.0.0.1','[::1]','localhost'].includes(url.hostname);
if (!['http:','https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || (url.pathname !== '/' && url.pathname !== '')) throw new MiddlewareError('invalid_endpoint');
if (!local && (!options.allowRemoteContent || url.protocol !== 'https:')) throw new MiddlewareError('remote_content_not_enabled');
for (const value of [options.deadlineMs, options.retrieveDeadlineMs]) {
if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) throw new MiddlewareError('invalid_deadline');
}
this.endpoint = url.origin;
this.options = { ...options };
this.mode = options.mode ?? 'compress';
this.fetcher = options.fetch ?? globalThis.fetch;
}
/** Prime capability discovery during app startup, outside the first model call. */
async ready(signal?: AbortSignal): Promise<Capabilities> {
signal?.throwIfAborted();
if (this.mode === 'off') throw new MiddlewareError('off');
const value = validateCapabilities(await this.http('capabilities', undefined, this.options.deadlineMs ?? 100, signal));
this.capsCache.value = value;
return value;
}
View on GitHub (pinned to 3ee70a1026)