angular/angular · error · Error
Bad URL - Cannot parse URL: ${url}
Error message
Bad URL - Cannot parse URL: ${url} What it means
After the double-slash check passes, parseAppUrl() delegates to UrlCodec.parse(url, serverBase); the codec signals failure by returning a string instead of a parsed object, and the shim converts that into 'Bad URL - Cannot parse URL'. It means the URL survived the first check but is still unparseable relative to the server base.
Source
Thrown at packages/common/upgrade/src/location_shim.ts:365
private getServerBase() {
const {protocol, hostname, port} = this.platformLocation;
const baseHref = this.locationStrategy.getBaseHref();
let url = `${protocol}//${hostname}${port ? ':' + port : ''}${baseHref || '/'}`;
return url.endsWith('/') ? url : url + '/';
}
private parseAppUrl(url: string) {
if (DOUBLE_SLASH_REGEX.test(url)) {
throw new Error(`Bad Path - URL cannot start with double slashes: ${url}`);
}
let prefixed = url.charAt(0) !== '/';
if (prefixed) {
url = '/' + url;
}
let match = this.urlCodec.parse(url, this.getServerBase());
if (typeof match === 'string') {
throw new Error(`Bad URL - Cannot parse URL: ${url}`);
}
let path =
prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname;
this.$$path = this.urlCodec.decodePath(path);
this.$$search = this.urlCodec.decodeSearch(match.search);
this.$$hash = this.urlCodec.decodeHash(match.hash);
// make sure path starts with '/';
if (this.$$path && this.$$path.charAt(0) !== '/') {
this.$$path = '/' + this.$$path;
}
}
/**
* Registers listeners for URL changes. This API is used to catch updates performed by the
* AngularJS framework. These changes are a subset of the `$locationChangeStart` and
* `$locationChangeSuccess` events which fire when AngularJS updates its internally-referenced
* version of the browser URL.View on GitHub (pinned to 51cb07e980)
Solutions
- Pass simple app URLs of the form '/path?query#hash' to $location
- Normalize/encode before setting: encodeURI on dynamic segments
- Verify the <base href> and LocationUpgradeModule serverBase match the URLs being parsed
Example fix
// before $location.url(externalUrl); // raw cross-origin/oddly-encoded URL // after const appUrl = new URL(externalUrl); $location.url(appUrl.pathname + appUrl.search + appUrl.hash);
Defensive patterns
Strategy: validation
Validate before calling
function parseableUrl(url: string, base: string): boolean {
try { new URL(url, base); return true; } catch { return false; }
}
if (!parseableUrl(candidate, serverBase)) throw new Error('Refusing unparseable URL');
$location.url(candidate); Type guard
function isWellFormedUrlCandidate(u: string): boolean {
try { new URL(u, 'http://localhost/'); return true; } catch { return false; }
} Prevention
- Set simple '/path?query#hash' values on $location instead of raw external URLs
- Encode dynamic segments before assembling URLs
- Keep <base href> consistent with the shim's serverBase in hybrid apps
When it happens
Trigger: $location.url('http://:8080/x') (malformed host:port), URLs with invalid percent-encodings that later decoding rejects, or unusual base tags making the codec return an error string in a hybrid upgrade app.
Common situations: Raw window.location or backend-supplied URLs pushed into $location during upgrade; <base href> mismatches; hand-assembled URL strings with bad encoding.
Related errors
- Bad Path - URL cannot start with double slashes: ${url}
- Invalid url "${url}", missing path prefix "${this.getServerB
- LocationProvider.search(): First argument must be a string o
- Configure LocationUpgrade through LocationUpgradeModule.conf
- BROWSER_ANIMATION_BUILDER_INJECTED_WITHOUT_ANIMATIONS
AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22).
Data as JSON: /api/errors/a2ad208c0fd8b709.
Report an issue: GitHub.