{"record":{"id":"3dbd4731272a60d7","repo":"JuliusBrussee/caveman","slug":"invalid-endpoint","errorCode":"invalid_endpoint","errorMessage":"invalid_endpoint","messagePattern":"invalid_endpoint","errorType":"error_code","errorClass":"MiddlewareError","httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/middleware/runtime.ts","lineNumber":95,"sourceCode":"  readonly mode: 'off' | 'record' | 'compress';\n  private readonly options: RuntimeOptions;\n  private readonly fetcher: typeof globalThis.fetch;\n  private readonly lifetime = new AbortController();\n  private readonly bindings = new WeakSet<RecoveryBinding>();\n  private readonly capsCache: { value: Capabilities | null } = { value: null };\n  private failures = 0;\n  private openUntil = 0;\n  private pending = 0;\n  private receiptsPending = 0;\n  private fetchesPending = 0;\n  private receiptFetchesPending = 0;\n  private receiptTail: Promise<void> = Promise.resolve();\n  private reported: CallReport | null = null;\n\n  constructor(options: RuntimeOptions = {}) {\n    const url = new URL(options.endpoint ?? 'http://127.0.0.1:8787');\n    const local = ['127.0.0.1','[::1]','localhost'].includes(url.hostname);\n    if (!['http:','https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || (url.pathname !== '/' && url.pathname !== '')) throw new MiddlewareError('invalid_endpoint');\n    if (!local && (!options.allowRemoteContent || url.protocol !== 'https:')) throw new MiddlewareError('remote_content_not_enabled');\n    for (const value of [options.deadlineMs, options.retrieveDeadlineMs]) {\n      if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) throw new MiddlewareError('invalid_deadline');\n    }\n    this.endpoint = url.origin;\n    this.options = { ...options };\n    this.mode = options.mode ?? 'compress';\n    this.fetcher = options.fetch ?? globalThis.fetch;\n  }\n\n  /** Prime capability discovery during app startup, outside the first model call. */\n  async ready(signal?: AbortSignal): Promise<Capabilities> {\n    signal?.throwIfAborted();\n    if (this.mode === 'off') throw new MiddlewareError('off');\n    const value = validateCapabilities(await this.http('capabilities', undefined, this.options.deadlineMs ?? 100, signal));\n    this.capsCache.value = value;\n    return value;\n  }","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/middleware/runtime.ts#L77-L113","documentation":"The middleware Runtime constructor validates the endpoint option as a strict HTTP(S) origin URL and throws MiddlewareError with code 'invalid_endpoint' if it fails any check: protocol must be http: or https:, no embedded username/password, no query string, no hash, and the pathname must be '/' or empty. The default is http://127.0.0.1:8787.","triggerScenarios":"new Runtime({ endpoint: '127.0.0.1:8787' }) (missing scheme), 'http://127.0.0.1:8787/api' (non-root path), 'http://user:pass@host/', 'https://host/?x=1' or 'https://host/#frag', or 'ftp://host/'. Also occurs when the endpoint string comes from an env var with a trailing path or whitespace-encoded characters that produce a non-empty pathname/query.","commonSituations":"Copying a full API URL (with path) into the endpoint config instead of a bare origin; forgetting the https:// scheme; config templates that append /v1 or /middleware to the endpoint; URL env vars with trailing '?' or '#' characters.","solutions":["Pass a bare origin: new Runtime({ endpoint: 'http://127.0.0.1:8787' }) — no path, query, hash, or credentials","Prepend the scheme if missing (https:// or http://)","Strip any path/query/fragment from the configured endpoint string before constructing Runtime","Validate the endpoint with the same URL checks in your config-loading layer and fail with a clear message"],"exampleFix":"// before\nconst rt = new Runtime({ endpoint: process.env.CAVE_MW_URL }); // 'http://host:8787/api'\n// after\nconst raw = new URL(process.env.CAVE_MW_URL);\nconst rt = new Runtime({ endpoint: raw.origin }); // 'http://host:8787'","handlingStrategy":"validation","validationCode":"function sanitizeEndpoint(raw: string): string {\n  const u = new URL(raw);\n  if (!['http:', 'https:'].includes(u.protocol) || u.username || u.password || u.search || u.hash || (u.pathname !== '/' && u.pathname !== '')) throw new Error(`endpoint must be a bare origin, got: ${raw}`);\n  return u.origin;\n}","typeGuard":"function isBareHttpOrigin(raw: string): boolean { try { const u = new URL(raw); return ['http:', 'https:'].includes(u.protocol) && !u.username && !u.password && !u.search && !u.hash && (u.pathname === '/' || u.pathname === ''); } catch { return false; } }","tryCatchPattern":"try { const rt = new Runtime({ endpoint }); } catch (e) { if (e instanceof MiddlewareError && e.code === 'invalid_endpoint') throw new Error(`CAVE_MW_URL must be a bare http(s) origin, got '${endpoint}'`); throw e; }","preventionTips":["Store endpoints as bare origins (scheme://host:port) in config/env","Trim and normalize endpoint strings at config load time","Never append paths like /v1 or /api to middleware endpoints","Validate with new URL() before passing to Runtime so failures surface in config parsing"],"tags":["config","url","middleware","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}