{"record":{"id":"23b88f3978c01415","repo":"googleapis/mcp-toolbox","slug":"invalid-page-url-w","errorCode":null,"errorMessage":"invalid page URL: %w","messagePattern":"invalid page URL: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudhealthcare/cloud_healthcare.go","lineNumber":327,"sourceCode":"\tif len(v) < 2 || v[0] != 'v' {\n\t\treturn false\n\t}\n\t// The character after 'v' must be a digit '1'-'9'\n\tif v[1] < '1' || v[1] > '9' {\n\t\treturn false\n\t}\n\tfor i := 2; i < len(v); i++ {\n\t\tif !isAlphanumeric(v[i]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (s *Source) validateFHIRPageURL(pageURL string) (string, error) {\n\tparsed, err := url.Parse(pageURL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid page URL: %w\", err)\n\t}\n\n\tif parsed.Scheme != \"https\" {\n\t\treturn \"\", fmt.Errorf(\"URL scheme must be https, got %q\", parsed.Scheme)\n\t}\n\n\tparsed.Host = strings.ToLower(parsed.Host)\n\thost := parsed.Host\n\tif h, _, err := net.SplitHostPort(host); err == nil {\n\t\thost = h\n\t}\n\tif _, ok := allowedFHIRHosts[host]; !ok {\n\t\treturn \"\", fmt.Errorf(\"URL host must be an allowed FHIR host, got %q\", host)\n\t}\n\n\t// Clean and split path\n\tcleanPath := path.Clean(parsed.Path)\n\t// Truncate leading and trailing slashes for easier splitting","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudhealthcare/cloud_healthcare.go#L309-L345","documentation":"validateFHIRPageURL parses a caller-provided pagination URL with url.Parse before following it. This error means the page URL string is not a syntactically valid URL at all (url.Parse returned an error). The library throws it to avoid blindly issuing requests to garbage or attacker-controlled strings passed as FHIR next-page tokens.","triggerScenarios":"FHIRFetchPage is given a nextPageToken/pageUrl parameter that is empty-with-garbage, contains control characters, or otherwise fails url.Parse (e.g. \"http://[::1\" with an unterminated bracket).","commonSituations":"An LLM/caller hands back a truncated or mangled page URL extracted from a previous response; a page token is confused with a full URL; shell/JSON escaping corrupts the URL (embedded spaces or newlines are usually tolerated by url.Parse, but malformed IPv6 brackets are not).","solutions":["Pass the page URL exactly as it appeared in the previous response's links.next field — do not truncate or re-encode it","Ensure the parameter is a full URL, not the opaque page token from a different API","Strip whitespace/control characters before passing the URL","If constructing programmatically, build with url.URL struct or url.BuildQuery and String() to guarantee validity"],"exampleFix":"// before\nnextURL := strings.TrimSpace(rawLink)\nresp, err := source.FHIRFetchPage(ctx, nextURL[1:]) // accidentally sliced\n// after\nnextURL := strings.TrimSpace(rawLink)\nif _, err := url.Parse(nextURL); err != nil {\n    return fmt.Errorf(\"page link from prior response is malformed: %w\", err)\n}\nresp, err := source.FHIRFetchPage(ctx, nextURL)","handlingStrategy":"validation","validationCode":"function isValidPageURL(u) { try { const p = new URL(u); return p.protocol === 'https:'; } catch { return false; } }","typeGuard":null,"tryCatchPattern":"try {\n  const page = await source.FHIRFetchPage(ctx, nextURL);\n} catch (e) {\n  if (/invalid page URL/.test(e.message)) {\n    console.error('Page link corrupted, restart pagination from first page');\n    return startFreshPagination();\n  }\n  throw e;\n}","preventionTips":["Always copy links.next verbatim from the prior FHIR response","Trim whitespace/control characters from URLs before passing them","Never truncate or re-encode the page URL (watch JSON escaping and shell quoting)","Persist the full URL, not just the pageToken, between pagination steps"],"tags":["url","validation","pagination","fhir"],"backgroundTag":"invalid-url-format","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}