skip to content

Search

Fixing Angular Status 0 Errors from CORS or Network Fails

3 min read

Debugging an Angular HttpInterceptor that returns status: 0 on failure. A look at XHR, CORS errors, and why the request never reaches the backend.

Last week, I had to debug an authentication issue and came across a status code of 0. I’d never seen this before so I asked GPT to help me understand what was going on.

The Setup

To reproduce the problem locally, first bootstrap a new Angular project with:

pnpm install -g @angular/cli
ng new my-app --package-manager=pnpm --defaults
cd my-app

Define an HttpInterceptor in src/app/http-error.interceptor.ts:

import { HttpInterceptorFn } from "@angular/common/http";
import { catchError, throwError } from "rxjs";
 
export const httpErrorInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    catchError((error) => {
      console.log("HTTP Error Status:", error.status);
      return throwError(() => error);
    })
  );
};

Inject the interceptor in src/app/app.config.ts:

import { ApplicationConfig, provideZoneChangeDetection } from "@angular/core";
import { provideRouter } from "@angular/router";
 
import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { routes } from "./app.routes";
import { httpErrorInterceptor } from "./http-error.interceptor";
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient(withInterceptors([httpErrorInterceptor])),
  ],
};

Add hitBackend() method in src/app/app.component.ts:

import { HttpClient } from "@angular/common/http";
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
 
@Component({
  selector: "app-root",
  imports: [RouterOutlet],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "my-app";
 
  constructor(private http: HttpClient) {}
 
  hitBackend() {
    this.http.get("http://localhost:3000").subscribe({
      next: (res) => console.log("Success", res),
      error: (err) => console.log("Fail", err),
    });
  }
}

Create a button to call it somewhere in src/app/app.component.html:

<button (click)="hitBackend()">Hit Backend (Triggers CORS Error)</button>

Lastly, run the frontend with:

ng serve

For backend, create server.mjs:

import { createServer } from "http";
 
const server = createServer((_req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("This will not be received by a browser on a different origin.");
});
 
server.listen(3000, () => {
  console.log("Server running at http://localhost:3000/");
});

And run the backend with:

node server.mjs

When I click the button, I see HTTP Error Status: 0 in browser console.

What I learned

Apparently, Angular’s HttpClient makes requests using XMLHttpRequest (XHR), which predates fetch() and can return 0 on fail. This was confusing on three levels:

  1. status can also return normal HTTP response codes (e.g., 200, 404), so 0 feels out of place.
  2. 0 is usually falsy in JavaScript/TypeScript, so if (status) can skip valid cases.
  3. 0 usually means “success” in Unix, which adds to the confusion.

Regardless, the status code of 0 in XHR implies the browser never hit the backend to receive any response. Scenarios where this could happen are:

  • Network failure
  • CORS error
  • Request blocked or aborted
  • Timeout

TL;DR

When using XHR, as in the case of Angular’s HttpClient, a status of 0 usually means the request never reached the server (e.g., due to CORS, network issues, or timeouts).

In fetch, network failures (like CORS or DNS errors) result in a TypeError, not a response with status: 0.