{"record":{"id":"5d2a39711b786dc9","repo":"actualbudget/actual","slug":"missing-url-parameter","errorCode":null,"errorMessage":"Missing url parameter","messagePattern":"Missing url parameter","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"packages/sync-server/src/app-cors-proxy.js","lineNumber":132,"sourceCode":"    console.warn('Invalid target URL:', targetUrl, e.message);\n    return false;\n  }\n}\n\napp.use('/', async (req, res) => {\n  // CORS preflight\n  if (req.method === 'OPTIONS') {\n    res.set('Access-Control-Allow-Origin', '*');\n    res.set('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS');\n    res.set('Access-Control-Allow-Headers', 'Content-Type, X-Actual-Token');\n    res.set('Access-Control-Max-Age', '600');\n    return res.status(204).end();\n  }\n\n  const targetUrlString = req.query.url;\n\n  if (!targetUrlString) {\n    return res.status(400).json({ error: 'Missing url parameter' });\n  }\n\n  // Validate session/token\n  const session = await validateSession(req, res);\n  if (!session) {\n    return; // validateSession already sent the response\n  }\n\n  let url;\n  try {\n    url = new URL(targetUrlString);\n  } catch {\n    return res.status(400).json({ error: 'Invalid url parameter' });\n  }\n\n  // Fetch the latest allowlist\n  try {\n    await fetchAllowlist();","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-cors-proxy.js#L114-L150","documentation":"The CORS proxy endpoint requires the target address to be supplied as the `url` query parameter. If the query string has no url parameter (or it is empty), the proxy immediately responds 400 with error 'Missing url parameter'. This is a cheap pre-check performed before session validation and allowlist checks.","triggerScenarios":"GET/POST to /cors-proxy without ?url=<encoded-url>, or with url= empty, e.g. calling the proxy endpoint directly without building the query string.","commonSituations":"Forgetting encodeURIComponent so a bare URL breaks the query string; constructing the request manually instead of using the client helper; proxy path hit by a health check or crawler without parameters.","solutions":["Append the target URL as a properly encoded query parameter: /cors-proxy?url=<encodeURIComponent(target)>.","Use the provided client-side proxy helper if available instead of hand-building the request.","Verify the request method/path matches what the plugin code expects (query param, not body field)."],"exampleFix":"// before\nfetch('/cors-proxy', { method: 'GET' });\n// after\nfetch(`/cors-proxy?url=${encodeURIComponent('https://example.com/list.json')}`);","handlingStrategy":"validation","validationCode":"if (!target) throw new Error('cors-proxy requires a target url');\nconst qs = `?url=${encodeURIComponent(target)}`;","typeGuard":null,"tryCatchPattern":"try {\n  return await fetch(`/cors-proxy?url=${encodeURIComponent(target)}`);\n} catch (e) {\n  if (e.status === 400) console.error('Check the proxy request: url query param required');\n  throw e;\n}","preventionTips":["Always use encodeURIComponent when placing the target in the query string.","Centralize proxy URL construction in one helper function.","Write a smoke test that hits the proxy with a valid url parameter."],"tags":["cors-proxy","rest-api","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}