{"record":{"id":"07513ca9f009b38e","repo":"laurent22/joplin","slug":"href-href-not-in-baseurl-baseurl-nor-relativ","errorCode":null,"errorMessage":"href ${href} not in baseUrl ${baseUrl} nor relativeBaseUrl ${relativeBaseUrl}","messagePattern":"href (.+?) not in baseUrl (.+?) nor relativeBaseUrl (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/lib/file-api-driver-webdav.js","lineNumber":113,"sourceCode":"\t\t\treturn result.items;\n\t\t};\n\n\t\treturn await basicDelta(path, getDirStats, options);\n\t}\n\n\t// A file href, as found in the result of a PROPFIND, can be either an absolute URL or a\n\t// relative URL (an absolute URL minus the protocol and domain), while the sync algorithm\n\t// works with paths relative to the base URL.\n\threfToRelativePath_(href, baseUrl, relativeBaseUrl) {\n\t\tlet output = '';\n\t\tif (href.indexOf(baseUrl) === 0) {\n\t\t\toutput = href.substr(baseUrl.length);\n\t\t} else if (href.indexOf(relativeBaseUrl) === 0) {\n\t\t\toutput = href.substr(relativeBaseUrl.length);\n\t\t} else if (decodeURIComponent(href).indexOf(decodeURIComponent(relativeBaseUrl)) === 0) {\n\t\t\toutput = decodeURIComponent(href).substring(decodeURIComponent(relativeBaseUrl).length);\n\t\t} else {\n\t\t\tthrow new Error(`href ${href} not in baseUrl ${baseUrl} nor relativeBaseUrl ${relativeBaseUrl}`);\n\t\t}\n\n\t\treturn rtrimSlashes(ltrimSlashes(output));\n\t}\n\n\tstatsFromResources_(resources) {\n\t\tconst relativeBaseUrl = this.api().relativeBaseUrl();\n\t\tconst baseUrl = this.api().baseUrl();\n\t\tconst output = [];\n\t\tfor (let i = 0; i < resources.length; i++) {\n\t\t\tconst resource = resources[i];\n\t\t\tconst href = this.api().stringFromJson(resource, ['d:href', 0]);\n\t\t\tconst path = this.hrefToRelativePath_(href, baseUrl, relativeBaseUrl);\n\t\t\t// if (href.indexOf(relativeBaseUrl) !== 0) throw new Error('Path \"' + href + '\" not inside base URL: ' + relativeBaseUrl);\n\t\t\t// const path = rtrimSlashes(ltrimSlashes(href.substr(relativeBaseUrl.length)));\n\t\t\tif (path === '') continue; // The list of resources includes the root dir too, which we don't want\n\t\t\tconst stat = this.statFromResource_(resources[i], path);\n\t\t\toutput.push(stat);","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/file-api-driver-webdav.js#L95-L131","documentation":"Thrown by hrefToRelativePath_() when converting a PROPFIND href into a sync-relative path. It tries three matches: the href starts with baseUrl, with relativeBaseUrl, or their percent-decoded forms. If none match, the href doesn't belong to the configured sync root and the driver can't map it, so it aborts to avoid silently mis-addressing files.","triggerScenarios":"list() or stat() returns a d:href that points outside the configured WebDAV base — e.g. the server rewrites hrefs to an absolute URL with a different host/path, returns a redirect target, or the configured base URL doesn't match the actual server URL.","commonSituations":"Base URL configured with http while server redirects to https (or vice-versa); behind a reverse proxy that rewrites the host/port; Nextcloud/Seafile returning hrefs with a different path prefix than the configured root; trailing-slash mismatch between config and server; CDN/front-end altering URLs.","solutions":["Make the configured WebDAV base URL exactly match the href prefix the server returns (same scheme, host, port, path).","If behind a reverse proxy, fix the proxy to pass through the original host/path or adjust the base URL to the proxy-visible value.","Ensure scheme consistency (use https if the server redirects to https).","Decode/encode percent-encoded characters in the base URL to match the server's encoding."],"exampleFix":"// before\nif (href.indexOf(baseUrl) === 0) { output = href.substr(baseUrl.length); }\nelse if (href.indexOf(relativeBaseUrl) === 0) { output = href.substr(relativeBaseUrl.length); }\nelse if (decodeURIComponent(href).indexOf(decodeURIComponent(relativeBaseUrl)) === 0) { output = decodeURIComponent(href).substring(decodeURIComponent(relativeBaseUrl).length); }\nelse { throw new Error(`href ${href} not in baseUrl ${baseUrl} nor relativeBaseUrl ${relativeBaseUrl}`); }\n// after - also tolerate scheme/port differences via URL parsing\nconst stripBase = (h, b) => { try { const H = new URL(h), B = new URL(b); if (H.pathname.indexOf(B.pathname) === 0) return H.pathname.slice(B.pathname.length); } catch {} return null; };\noutput = stripBase(href, baseUrl) ?? stripBase(href, relativeBaseUrl);\nif (output === null) throw new Error(`href ${href} not under base ${baseUrl}`);","handlingStrategy":"validation","validationCode":"// Ensure the configured base URL matches the server's href prefix.\nfunction baseUrlMatches(base: string, sampleHref: string): boolean {\n  try {\n    const B = new URL(base), H = new URL(sampleHref);\n    return H.pathname.indexOf(B.pathname) === 0;\n  } catch { return sampleHref.indexOf(base) === 0; }\n}\nif (!baseUrlMatches(configuredBaseUrl, observedHref)) {\n  throw new Error('Base URL does not match server href prefix; fix sync config.');\n}","typeGuard":"function hrefBelongsToBase(href: string, baseUrl: string, relativeBaseUrl: string): boolean {\n  return href.indexOf(baseUrl) === 0 || href.indexOf(relativeBaseUrl) === 0\n    || decodeURIComponent(href).indexOf(decodeURIComponent(relativeBaseUrl)) === 0;\n}","tryCatchPattern":"try {\n  return await driver.list(path);\n} catch (e) {\n  if (/not in baseUrl/.test(e.message)) {\n    throw new Error('Sync stopped: server hrefs do not match the configured WebDAV base URL. Re-link with the correct URL.');\n  }\n  throw e;\n}","preventionTips":["Make the configured base URL exactly match the server (scheme, host, port, path).","Use https consistently if the server redirects http->https.","Reverse-proxy setups: align the proxy host/port with the configured URL.","Watch trailing-slash and percent-encoding mismatches."],"tags":["webdav","sync","url","config","propfind"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}