{"record":{"id":"203527ba39b92931","repo":"googleapis/mcp-toolbox","slug":"failed-to-construct-introspection-url-w","errorCode":null,"errorMessage":"failed to construct introspection URL: %w","messagePattern":"failed to construct introspection URL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/generic/generic.go","lineNumber":353,"sourceCode":"\terr = a.validateClaims(ctx, iss, aud, scopeClaim)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn claims, nil\n}\n\n// validateOpaqueToken validates an opaque token by calling the introspection endpoint\nfunc (a AuthService) validateOpaqueToken(ctx context.Context, tokenStr string) (map[string]any, error) {\n\tlogger, err := util.LoggerFromContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get logger from context: %w\", err)\n\t}\n\n\tintrospectionURL := a.introspectionURL\n\tif introspectionURL == \"\" {\n\t\tintrospectionURL, err = url.JoinPath(a.AuthorizationServer, \"introspect\")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to construct introspection URL: %w\", err)\n\t\t}\n\t}\n\n\tparamName := a.IntrospectionParamName\n\tif paramName == \"\" {\n\t\tparamName = \"token\"\n\t}\n\n\tvar req *http.Request\n\tif a.IntrospectionMethod == \"GET\" {\n\t\tu, err := url.Parse(introspectionURL)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse introspection URL: %w\", err)\n\t\t}\n\t\tq := u.Query()\n\t\tq.Set(paramName, tokenStr)\n\t\tu.RawQuery = q.Encode()\n\t\treq, err = http.NewRequestWithContext(ctx, \"GET\", u.String(), nil)","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/auth/generic/generic.go#L335-L371","documentation":"When no explicit introspection URL is configured, validateOpaqueToken derives one by joining the configured AuthorizationServer base URL with the 'introspect' path using url.JoinPath. If the join fails (malformed base URL, control characters, invalid escape), the library wraps the error with this message and aborts validation.","triggerScenarios":"AuthService.Config.IntrospectionURL is empty and url.JoinPath(a.AuthorizationServer, \"introspect\") returns an error, e.g. AuthorizationServer set to an invalid value like 'http://[::1' or containing control characters.","commonSituations":"Typo or truncation in the AuthorizationServer config value, environment variable interpolation injecting whitespace or quotes, or forgetting the scheme (e.g. 'idp.example.com' instead of 'https://idp.example.com').","solutions":["Check the configured authorizationServer/authUrl value and fix the malformed base URL (include scheme, valid host).","Echo/print the resolved env var to catch stray spaces, quotes, or newline characters.","Alternatively set the explicit introspection URL in the auth config to bypass JoinPath derivation."],"exampleFix":"// before\ngenericAuth:\n  authorizationServer: \"idp.example.com/oauth2\"  # missing scheme\n// after\ngenericAuth:\n  authorizationServer: \"https://idp.example.com/oauth2\"","handlingStrategy":"validation","validationCode":"base := cfg.AuthorizationServer\nu, err := url.Parse(base)\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"authorizationServer %q is not a valid absolute URL\", base)\n}","typeGuard":"func isValidAbsoluteURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && (u.Scheme == \"http\" || u.Scheme == \"https\") && u.Host != \"\"\n}","tryCatchPattern":"claims, err := svc.ValidateMCPAuth(ctx, header)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to construct introspection URL\") {\n        log.Fatalf(\"bad authorizationServer config: %v\", err) // startup config bug, not runtime\n    }\n    return claims, err\n}","preventionTips":["Validate the auth URL config at startup with url.Parse","Always include the https:// scheme in configuration values","Quote env vars in shell/YAML to avoid injected whitespace"],"tags":["configuration","url","oauth"],"backgroundTag":"invalid-url-config","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"}