{"record":{"id":"483b5e6316192224","repo":"abhigyanpatwari/GitNexus","slug":"provide-url-git-url-or-path-local-path","errorCode":null,"errorMessage":"Provide \"url\" (git URL) or \"path\" (local path)","messagePattern":"Provide \"url\" \\(git URL\\) or \"path\" \\(local path\\)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"gitnexus/src/server/api.ts","lineNumber":1515,"sourceCode":"          path: repoLocalPath,\n          force,\n          embeddings,\n          dropEmbeddings,\n          token: repoToken,\n        } = req.body;\n\n        // Input type validation\n        if (repoUrl !== undefined && typeof repoUrl !== 'string') {\n          res.status(400).json({ error: '\"url\" must be a string' });\n          return;\n        }\n        if (repoLocalPath !== undefined && typeof repoLocalPath !== 'string') {\n          res.status(400).json({ error: '\"path\" must be a string' });\n          return;\n        }\n\n        if (!repoUrl && !repoLocalPath) {\n          res.status(400).json({ error: 'Provide \"url\" (git URL) or \"path\" (local path)' });\n          return;\n        }\n\n        // Token: optional, restricted charset to prevent header smuggling\n        // (CRLF), bound length, and bound to github.com (see validateAnalyzeToken).\n        const tokenError = validateAnalyzeToken(repoToken, repoUrl);\n        if (tokenError) {\n          res.status(tokenError.status).json({ error: tokenError.error });\n          return;\n        }\n\n        // Path validation. The previous `normalize !== resolve` guard was inert\n        // (both collapse `..` identically) and only false-rejected trailing\n        // slashes, so it is dropped. Analyzing a local path the operator names\n        // is the tool's intended capability (same as the CLI); the dangerous\n        // part was cross-origin reach, which is closed by requireTrustedOrigin\n        // on this route (scoped to loopback, the server's own bound host, and a\n        // configured GITNEXUS_PUBLIC_ORIGIN — other LAN devices are NOT","sourceCodeStart":1497,"sourceCodeEnd":1533,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/api.ts#L1497-L1533","documentation":"HTTP 400 returned by POST /api/analyze when the body provides neither \"url\" nor \"path\" (the check is falsy-based, so empty strings count as missing too). The route requires exactly one analysis source: a git URL to clone or an absolute local path to index; a body carrying only auxiliary fields (token, force, embeddings, dropEmbeddings) is not enough.","triggerScenarios":"POSTing {} or {\"token\":\"...\"} or {\"force\":true}; sending the parameters as a query string (?url=...) or form-data so express.json() never populates req.body; typo'd or renamed keys such as \"repo\", \"uri\", \"localPath\" that this route never reads.","commonSituations":"Missing Content-Type: application/json header so the JSON body is skipped by the parser; client refactors renaming fields; copy-pasted curl with parameters in the URL instead of -d; contract tests posting an empty object.","solutions":["Include exactly one of {\"url\": \"<git-url>\"} or {\"path\": \"</absolute/path>\"} in the JSON body","Set Content-Type: application/json — without it req.body stays empty and every field reads as missing","Check for renamed keys: this route only reads url, path, force, embeddings, dropEmbeddings, token","Smoke-test with curl: curl -X POST http://localhost:4747/api/analyze -H 'Content-Type: application/json' -d '{\"url\":\"https://github.com/org/repo\"}'"],"exampleFix":"// before\nawait fetch('/api/analyze', {\n  method: 'POST',\n  body: JSON.stringify({ repo: 'org/repo' }), // wrong key, no content-type\n});\n\n// after\nawait fetch('/api/analyze', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ url: 'https://github.com/org/repo' }),\n});","handlingStrategy":"validation","validationCode":"// Exactly one source must be present before sending\nconst hasSource = (b: Record<string, unknown>) =>\n  (typeof b.url === 'string' && b.url !== '') ||\n  (typeof b.path === 'string' && b.path !== '');\nif (!hasSource(body)) throw new Error('Provide \"url\" (git URL) or \"path\" (local path)');","typeGuard":"const isAnalyzeRequest = (b: unknown): b is { url: string } | { path: string } => {\n  if (typeof b !== 'object' || b === null) return false;\n  const o = b as Record<string, unknown>;\n  return (\n    (typeof o.url === 'string' && o.url !== '') ||\n    (typeof o.path === 'string' && o.path !== '')\n  );\n};","tryCatchPattern":null,"preventionTips":["Always send Content-Type: application/json — an unparsed body fails every presence check","Use the exact keys url/path; repo, uri, localPath are ignored","Validate presence in the request builder, not after the 400 round-trip"],"tags":["http-400","validation","missing-argument","request-body","api"],"backgroundTag":"missing-required-parameter","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}