{"record":{"id":"d5999bae7cd5ac08","repo":"musistudio/claude-code-router","slug":"new-api-provider-base-url-is-missing","errorCode":null,"errorMessage":"New API provider base URL is missing.","messagePattern":"New API provider base URL is missing\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/electron/bundled-plugins/new-api-account/index.cjs","lineNumber":30,"sourceCode":"      providerAccountConnectors: [\n        {\n          id: CONNECTOR_ID,\n          resolve: resolveSubscriptionSelf\n        }\n      ]\n    };\n  }\n};\n\nasync function resolveSubscriptionSelf(request) {\n  if (typeof request.fetchProviderAccountJson !== \"function\") {\n    throw new Error(\"New API account connector requires CCR Desktop browser account fetch support.\");\n  }\n\n  const options = isRecord(request.connector?.options) ? request.connector.options : {};\n  const root = newApiRootBaseUrl(readString(options.baseUrl) || providerBaseUrl(request.provider));\n  if (!root) {\n    throw new Error(\"New API provider base URL is missing.\");\n  }\n\n  const timeoutMs = normalizeTimeoutMs(options.timeoutMs);\n  const requestOrigin = readString(options.requestOrigin) || root;\n  const refreshPayload = await request.fetchProviderAccountJson({\n    body: options.refreshBody ?? {},\n    credentials: \"include\",\n    endpoint: rootUrl(root, readString(options.refreshPath) || DEFAULT_REFRESH_PATH),\n    headers: readStringRecord(options.refreshHeaders),\n    method: \"POST\",\n    requestOrigin,\n    timeoutMs\n  });\n  assertSuccessfulPayload(refreshPayload, \"New API refresh\");\n\n  const token = refreshTokenFromPayload(refreshPayload);\n  if (!token) {\n    throw new Error(\"New API refresh response did not include an access token.\");","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/electron/bundled-plugins/new-api-account/index.cjs#L12-L48","documentation":"The connector computes the provider root URL from options.baseUrl or the provider record's base URL; if neither yields a usable root via newApiRootBaseUrl, it throws. This is a connector configuration problem, not a network error.","triggerScenarios":"options.baseUrl absent/empty/whitespace AND provider.baseUrl missing or unparseable; baseUrl with only a scheme like 'https://' or a path that normalizes away.","commonSituations":"Connector created without setting baseUrl, provider record malformed after import/migration, typo like 'htp://api.example.com', or trailing-slash/query-only URLs rejected by normalization.","solutions":["Set connector.options.baseUrl to a valid absolute URL, e.g. https://api.example.com","Verify the provider record passed in request.provider actually contains its base URL","Trim/validate baseUrl at connector-save time and reject empty values early","Check newApiRootBaseUrl's accepted formats if using an unusual URL shape"],"exampleFix":"// before\nconst options = {}; // no baseUrl, provider.baseUrl undefined -> throws\n\n// after\nconst options = { baseUrl: 'https://api.example.com' };","handlingStrategy":"validation","validationCode":"const root = newApiRootBaseUrl(readString(options.baseUrl) || providerBaseUrl(provider));\nif (!root) throw new ConfigError('Set connector options.baseUrl to e.g. https://api.example.com');","typeGuard":"function isValidBaseUrl(v: unknown): v is string {\n  try { const u = new URL(String(v)); return u.protocol === 'https:' || u.protocol === 'http:'; } catch { return false; }\n}","tryCatchPattern":"null","preventionTips":["Validate baseUrl with new URL() when saving connector config","Default provider base URLs in provider templates","Reject empty/whitespace baseUrl in forms"],"tags":["configuration","base-url","validation"],"backgroundTag":"missing-env-var","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}