{"record":{"id":"ccd02f7da223ce65","repo":"nexu-io/open-design","slug":"public-github-repository-metric-input-url-must-be","errorCode":null,"errorMessage":"public_github_repository_metric input.url must be a valid URL","messagePattern":"public_github_repository_metric input\\.url must be a valid URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/refresh.ts","lineNumber":657,"sourceCode":"  return asBoundedRefreshOutput({\n    toolName: 'git.summary',\n    isRepository: true,\n    branch: branch.trim(),\n    status: compactExecOutput(status),\n    recentCommits: compactExecOutput(recentCommits),\n    diffStat: compactExecOutput(diffStat),\n  });\n}\n\nfunction selectGithubRepositoryApiUrl(input: PublicGithubRepositoryMetricInput): URL {\n  const rawUrl = optionalString(input.url, 'input.url');\n  if (rawUrl === undefined) throw new Error('public_github_repository_metric requires input.url');\n\n  let url: URL;\n  try {\n    url = new URL(rawUrl);\n  } catch {\n    throw new Error('public_github_repository_metric input.url must be a valid URL');\n  }\n\n  if (url.protocol !== 'https:' || url.hostname !== 'api.github.com') {\n    throw new Error('public_github_repository_metric only supports https://api.github.com repository URLs');\n  }\n  if (!/^\\/repos\\/[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/.test(url.pathname)) {\n    throw new Error('public_github_repository_metric only supports /repos/{owner}/{repo} URLs');\n  }\n  url.search = '';\n  url.hash = '';\n  url.username = '';\n  url.password = '';\n  return url;\n}\n\nfunction selectGithubFields(input: PublicGithubRepositoryMetricInput): string[] {\n  if (input.fields === undefined) return ['stargazers_count', 'full_name', 'html_url', 'updated_at'];\n  if (!Array.isArray(input.fields)) throw new Error('input.fields must be an array of strings');","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/refresh.ts#L639-L675","documentation":"Thrown by selectGithubRepositoryApiUrl when the input.url string cannot be parsed by the WHATWG URL constructor (new URL). This is the first validation gate for the public_github_repository_metric refresh source: the value must be a syntactically valid absolute URL before any host/path checks run. It exists because live-artifact refresh inputs are model-authored JSON, so untrusted/malformed strings must be rejected before any network call.","triggerScenarios":"A live artifact refresh source of type daemon_tool with toolName 'public_github_repository_metric' whose input.url is undefined-ish handled elsewhere but defined as empty string '', a relative path like 'repos/foo/bar', a malformed string like 'ht!tp://api.github.com', or any value new URL() rejects.","commonSituations":"Model emits input.url without a scheme (e.g. 'api.github.com/repos/...'); agent writes a GitHub web URL intending it as API URL but typos the scheme; JSON template used a placeholder like '${URL}' that was never substituted; trailing whitespace or invisible characters from a copied cell.","solutions":["Set input.url to a full absolute URL beginning with 'https://', e.g. 'https://api.github.com/repos/<owner>/<repo>'.","If the value comes from a template substitution, verify the variable is bound and non-empty before refresh runs.","Trim the string and reject empty before constructing the source so the error surfaces at authoring time, not refresh time."],"exampleFix":"// before\ninput: { url: 'repos/octocat/Hello-World' }\n// after\ninput: { url: 'https://api.github.com/repos/octocat/Hello-World' }","handlingStrategy":"validation","validationCode":"function assertGithubApiUrl(raw: unknown): URL {\n  if (typeof raw !== 'string' || raw.length === 0) throw new Error('input.url required');\n  let u: URL;\n  try { u = new URL(raw); } catch { throw new Error('input.url is not a valid URL'); }\n  return u;\n}\n// call before building the refresh source","typeGuard":"function isUrlString(v: unknown): v is string {\n  if (typeof v !== 'string' || v.length === 0) return false;\n  try { new URL(v); return true; } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Author refresh sources through a builder that validates input.url with new URL() before emitting JSON.","Strip whitespace and reject empty before constructing the source.","Treat URL validation as an authoring-time concern, not just a runtime check."],"tags":["validation","url-parsing","live-artifacts","github-api"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}