{"record":{"id":"8a646ee42d9c225d","repo":"NationalSecurityAgency/ghidra","slug":"url-is-missing-a-repository-path","errorCode":null,"errorMessage":"URL is missing a repository path","messagePattern":"URL is missing a repository path","errorType":"validation","errorClass":"MalformedURLException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java","lineNumber":93,"sourceCode":"\t * @throws MalformedURLException if the given URL string cannot be parsed\n\t * @throws URISyntaxException if the given URL string cannot be parsed\n\t * @throws IllegalArgumentException if local ghidra URL is specified\n\t */\n\tpublic static URL deriveBSimURL(String urlString)\n\t\t\tthrows IllegalArgumentException, MalformedURLException, URISyntaxException {\n\t\tURL url = new URI(urlString).toURL();\t// URL used only for parsing purposes\n\t\tString protocol = url.getProtocol();\n\t\tif (\"postgresql\".equals(protocol) || \"https\".equals(protocol) ||\n\t\t\t\"elastic\".equals(protocol) || \"file\".equals(protocol)) {\n\t\t\tcheckBSimServerURL(url);\n\t\t\treturn url; // URL already corresponds to BSim server protocol\n\t\t}\n\t\tif (!GhidraURL.isServerRepositoryURL(url)) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to infer BSim URL from: \" + url);\n\t\t}\n\t\tString path = url.getPath();\t\t\t\t\t\t\t// Get the full path of the URL\n\t\tif (path == null || path.length() == 0 || path.equals(\"/\")) {\t\t// There must always be some kind of path, so we can derive the repository\n\t\t\tthrow new MalformedURLException(\"URL is missing a repository path\");\n\t\t}\n\t\tint endrepos = path.indexOf('/', 1);\t// Find the end of the first level of the path\n\t\tString repositoryURL;\n\t\tif (url.getProtocol().equals(GhidraURL.PROTOCOL)) {\t// Is this a ghidra URL\n\t\t\t// TODO: we could set things up so that a ghidra server could be queried for its associated BSim server\n\t\t\t// \"ghidra://host/repo?service=bsim\"\n\t\t\t// String repositoryURL = \"ghidra://\" + ghidraURL.getAuthority() + \"?service=bsim\";\n\n\t\t\t// Currently, all we do is assume that the BSim server is a PostgreSQL server\n\t\t\t// on the same host and with the same repo name as the ghidra server\n\t\t\trepositoryURL = \"postgresql://\" + url.getHost();\t\t// Just use the hostname\n\t\t}\n\t\telse {\n\t\t\t// For all other URL forms, we assume we are being handed the protocol and hostname (authority)\n\t\t\t// explicitly. We keep them for the final BSim URL\n\t\t\trepositoryURL = url.getProtocol() + \"://\" + url.getAuthority();\n\t\t}\n\t\t// Attach the first level of the path, which indicates the repository","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java#L75-L111","documentation":"Inside deriveBSimURL(), after confirming the URL is a Ghidra server repository URL, it checks the path. If the path is null, empty, or '/' it throws MalformedURLException because deriving the BSim repository name requires at least one path level (the repository name). Without a path, there's nothing to map to a BSim database name.","triggerScenarios":"Calling deriveBSimURL(urlString) where the URL passes isServerRepositoryURL (ghidra:// scheme) but getPath() is null/empty/'/'. The ghidra URL has no repository path component.","commonSituations":"A ghidra:// URL pointing only to a server root without a repository name (e.g., 'ghidra://myhost/'); malformed ghidra URL construction that dropped the path; URL constructed from host-only configuration.","solutions":["Include the repository name in the ghidra URL path: 'ghidra://myhost/repositoryName'.","Verify the ghidra URL construction includes the repository path segment.","Check that the configuration source providing the URL includes the repository name, not just the host."],"exampleFix":"// before\nURL url = BSimClientFactory.deriveBSimURL(\"ghidra://myhost/\"); // no repo path\n//\n// after\nURL url = BSimClientFactory.deriveBSimURL(\"ghidra://myhost/MyRepository\");","handlingStrategy":"validation","validationCode":"URL test = new URI(urlString).toURL();\nif (GhidraURL.isServerRepositoryURL(test)) {\n    String path = test.getPath();\n    if (path == null || path.isEmpty() || path.equals(\"/\")) {\n        // ghidra URL must include the repository name in the path\n        throw new IllegalArgumentException(\n            \"Ghidra URL must include a repository name: ghidra://host/repository\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    BSimClientFactory.deriveBSimURL(urlString);\n} catch (MalformedURLException e) {\n    if (e.getMessage().contains(\"missing a repository path\")) {\n        // prompt user to include the repository name\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always include the repository name in ghidra:// URLs.","Validate the path component is non-empty before deriving a BSim URL.","Provide ghidra:// URL templates showing the repository segment."],"tags":["bsim","url-parsing","ghidra-server","repository","configuration"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}