Skip to content

HttpTestingControler does not convert paths to a full URL on HttpErrorResponse #64725

@ThomasHardwickRocket

Description

@ThomasHardwickRocket

Which @angular/* package(s) are the source of the bug?

common

Is this a regression?

No

Description

This came up when testing an authentication http interceptor.
auth.interceptor.ts

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    catchError((err, caught) => {
      if (err instanceof HttpErrorResponse && err.status === 401 && new URL(err.url).pathname !== '/api/logon') {
        return doLogonAndRetry(err, caught);
      }
      throw err;
    })
  );
};

Even if just the path specified when the request was made, when a 401 response comes back from the server err.url contains a full URL e.g. https://angular.example/api/logon. So the above code works with a service like:
auth.service.ts

@Injectable({providedIn: 'root'})
export class AuthService {
  private readonly http = inject(HttpClient);
  logon(username: string, password: string) {
    return this.http.post('/api/logon', {username, password});
  }
  validateSession(username: string, password: string) {
    return this.http.get('/api/whoami');
  }
}

However in a test using the HttpTestingController e.g.
auth.interceptor.spec.ts

it('should not intercept logon requests') {
  const http = TestBed.inject(HttpTestingController);
  const authService = TestBed.inject(AuthService);
  let receivedError: unknown;
  authService.logon('user', 'pass').subscribe({
    error: (error) => receivedError = error;
  });
  const req = controller.expectOne('/api/logon');
  req.flush({}, {status: 401, statusText: 'Unauthorized'});
  expect(receivedError).toBeInstanceOf(HttpErrorResponse);
  if (receivedError instanceof HttpErrorResponse) {
    expect(receivedError.status).toBe(401);
    expect(receivedError.statusText).toBe('Unauthorized');
  }
});

Instead receivedError is "TypeError: Failed to construct 'URL': Invalid URL". This is because err.url only contains the string /logon.
I would expect HttpTestingController to emulate the behavior of connecting to an actual server as much as possible and so would expect the path to be expanded in to a full URL.
This also means I cannot use existing services to test the interceptor in this case.

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

TypeError: Failed to construct 'URL': Invalid URL

Please provide the environment you discovered this bug in (run ng version)

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 20.1.3
Node: 22.13.1
Package Manager: npm 10.9.2
OS: win32 x64

Angular: 20.1.3
... build, cdk, cli, common, compiler, compiler-cli, core, forms
... platform-browser, router

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.2001.3
@angular-devkit/core         20.1.3
@angular-devkit/schematics   20.1.3
@schematics/angular          20.1.3
rxjs                         7.8.2
typescript                   5.8.3

Repoduction

https://stackblitz.com/edit/stackblitz-starters-oo4yziqx?file=index.js
First try running in two terminals npm run backend and npm start.
You can see the error has the full URL.
Then try running npm run test you can see the URL is the relative path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: common/httpIssues related to HTTP and HTTP Clientneeds reproductionThis issue needs a reproduction in order for the team to investigate further

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions