{"record":{"id":"4f95206d16fd09cd","repo":"LykosAI/StabilityMatrix","slug":"relative-url-did-not-contain-a-route-path","errorCode":null,"errorMessage":"Relative URL did not contain a route path","messagePattern":"Relative URL did not contain a route path","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"StabilityMatrix.Core/Api/CivArchiveApiClient.cs","lineNumber":337,"sourceCode":"        {\n            query.Add($\"type={Uri.EscapeDataString(string.Join(\",\", filters.Types))}\");\n        }\n\n        if (filters.BaseModels.Count > 0)\n        {\n            query.Add($\"base_model={Uri.EscapeDataString(string.Join(\",\", filters.BaseModels))}\");\n        }\n\n        return $\"{NormalizeRoutePath(routePath)}.json?{string.Join(\"&\", query)}\";\n    }\n\n    public static string BuildDetailDataPath(string relativeUrl)\n    {\n        var uri = new Uri(BaseUri, relativeUrl);\n        var path = uri.AbsolutePath.TrimEnd('/');\n        if (string.IsNullOrWhiteSpace(path))\n        {\n            throw new InvalidOperationException(\"Relative URL did not contain a route path\");\n        }\n\n        // Apply Next.js rewrite rules: /{platform}/models/{id}/versions/{versionId}\n        // rewrites to /models/{id}?modelVersionId={versionId}&platform={platform}\n        var rewriteMatch = PlatformDetailRewriteRegex().Match(path);\n        if (rewriteMatch.Success)\n        {\n            var platform = rewriteMatch.Groups[\"platform\"].Value;\n            var modelId = rewriteMatch.Groups[\"modelId\"].Value;\n            var versionId = rewriteMatch.Groups[\"versionId\"].Value;\n\n            return $\"/models/{modelId}.json?modelVersionId={versionId}&platform={platform}\";\n        }\n\n        // Also handle /{platform}/models/{id} (without version)\n        var platformModelMatch = PlatformModelRewriteRegex().Match(path);\n        if (platformModelMatch.Success)\n        {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/LykosAI/StabilityMatrix/blob/af93d6ef57c01cd890d7e0ad0a9ea8c9fcda3002/StabilityMatrix.Core/Api/CivArchiveApiClient.cs#L319-L355","documentation":"BuildDetailDataPath converts a relative model URL into the Next.js data path (e.g. /models/{id}.json) and throws InvalidOperationException when the resulting URL path is empty after trimming trailing slashes — meaning the relative URL had no route component.","triggerScenarios":"Calling BuildDetailDataPath (directly or via GetModelDetailsAsync) with a URL like \"\", \"?query=1\", or a fragment-only reference so uri.AbsolutePath resolves to \"/\" and trims to empty.","commonSituations":"Passing query-string or fragment strings instead of route paths, malformed stored URLs missing the path component, base-URI-only references.","solutions":["Pass a URL containing a route path, e.g. \"/models/12345\"","Strip query/fragment and rebuild from the model ID if only those parts exist","Pre-validate that the URL's path is non-empty before calling"],"exampleFix":"// before\nBuildDetailDataPath(\"?modelVersionId=99\");\n// after\nBuildDetailDataPath(\"/models/12345?modelVersionId=99\");","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(url) || !url.TrimStart('/').Split('?')[0].Split('#')[0].Contains('/')) throw new ArgumentException(\"URL has no route path\");","typeGuard":"bool HasRoutePath(string? url) => !string.IsNullOrWhiteSpace(url) && new Uri(CivArchiveApiClient.BaseUri, url).AbsolutePath.TrimEnd('/').Length > 0;","tryCatchPattern":"try { var path = CivArchiveApiClient.BuildDetailDataPath(url); }\ncatch (InvalidOperationException ex) { Logger.Warn(ex, \"URL {Url} has no route path\", url); return null; }","preventionTips":["Always pass route paths like /models/{id}, not query or fragment strings","Normalize stored URLs on ingest","Reuse BuildDetailDataPath's logic to validate URLs early","Include the query string after the path, never instead of it"],"tags":["url","argument-validation","nextjs"],"backgroundTag":"invalid-url-format","analyzedSha":"af93d6ef57c01cd890d7e0ad0a9ea8c9fcda3002","analyzedAt":"2026-09-12T19:02:43.389Z","contentChangedAt":"2026-09-12T19:02:43.389Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}