Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 - Http request error: You provided 'undefined' where a stream was expected

While trying to do a HTTP Post request I am receiving the following error:

auth.service.ts?c694:156 Something went wrong requesting a new password, error message: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

The Http request is preceded by another one, which works perfectly fine.

My component calling the service:

requestNewPassword(formInput: NgForm) {
     if(formInput.controls['email'].value != '')
      this.authService.getApplicationAccessToken()
      .mergeMap((response: IAuthAppResponse) => this.authService.requestNewPassword(formInput, response.access_token))
      .subscribe(response => {
        // Content removed for simplicity
      })
    }

The service method throwing the error:

public requestNewPassword(formData: NgForm, applicationAccessToken: string): Observable<any> {
      let headers = new HttpHeaders({
        'Authorization':'Bearer ' + applicationAccessToken
      });
      let email: string = formData.controls['email'].value;
      const body = {
        email: email
      };

      console.log('requestNewPassword call header: ' + headers.get('Authorization'));
      console.log('Email: ' + body.email);

      return this.http.post(this.baseUrl + '/api/user/password/forgot', body, {headers}).do(response => {
        console.log("New password was successfully sent to the e-mail adress");
      }).catch((error: HttpErrorResponse) => {
        console.log('Something went wrong requesting a new password, error message: ' + error.message);
        return Observable.throw(error);
      })
    }

Whenever I enter an email in the form and submit, which in turn triggers the requestNewPassword method of the component, I receive the error mentioned above from the service.

The header and email are logged correctly so I don't think there's anything with the data I am providing.

Since I have no idea how to debug this, I thought it was a good idea to post this as a question on this incredible platform.

Thanks in advance!

Update

I minimized the code in my component to narrow down the problem by not chaining the two HTTP requests but only call the second one, that is causing trouble.

requestNewPassword(formInput: NgForm) {
      if(formInput.controls['email'].value != '')
      this.authService.requestNewPassword(formInput, "")
       .subscribe(response => {
         // Content removed for simplicity
       })
     }

I now get a full stack trace:

ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
    at Object.subscribeToResult (subscribeToResult.js?c011:74)
    at MergeMapSubscriber._innerSub (mergeMap.js?e2a2:132)
    at MergeMapSubscriber._tryNext (mergeMap.js?e2a2:129)
    at MergeMapSubscriber._next (mergeMap.js?e2a2:112)
    at MergeMapSubscriber.Subscriber.next (Subscriber.js?215e:89)
    at ScalarObservable._subscribe (ScalarObservable.js?d097:49)
    at ScalarObservable.Observable._trySubscribe (Observable.js?4e06:172)
    at ScalarObservable.Observable.subscribe (Observable.js?4e06:160)
    at MergeMapOperator.call (mergeMap.js?e2a2:87)
    at Observable.subscribe (Observable.js?4e06:157)
    at FilterOperator.call (filter.js?35ab:60)
    at Observable.subscribe (Observable.js?4e06:157)
    at MapOperator.call (map.js?c4af:56)
    at Observable.subscribe (Observable.js?4e06:157)
    at DoOperator.call (tap.js?7fa8:63)
    at Observable.subscribe (Observable.js?4e06:157)
    at CatchOperator.call (catchError.js?0867:79)
    at Observable.subscribe (Observable.js?4e06:157)
    at PasswordResetComponent.requestNewPassword (passwordReset.component.ts?c169:43)
    at Object.eval [as handleEvent] (PasswordResetComponent.html:7)
    at handleEvent (core.js?223c:13255)
    at callWithDebugContext (core.js?223c:14740)
    at Object.debugHandleEvent [as handleEvent] (core.js?223c:14327)
    at dispatchEvent (core.js?223c:9704)
    at eval (core.js?223c:12028)
    at SafeSubscriber.schedulerFn [as _next] (core.js?223c:4235)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js?215e:238)
    at SafeSubscriber.next (Subscriber.js?215e:185)
    at Subscriber._next (Subscriber.js?215e:125)
    at Subscriber.next (Subscriber.js?215e:89)
    at EventEmitter.Subject.next (Subject.js?c1c6:55)
    at EventEmitter.emit (core.js?223c:4203)
    at NgForm.onSubmit (forms.js?ad57:5710)
    at Object.eval [as handleEvent] (PasswordResetComponent.html:7)
    at handleEvent (core.js?223c:13255)
    at callWithDebugContext (core.js?223c:14740)
    at Object.debugHandleEvent [as handleEvent] (core.js?223c:14327)
    at dispatchEvent (core.js?223c:9704)
    at eval (core.js?223c:10318)
    at HTMLFormElement.eval (platform-browser.js?023b:2614)
    at ZoneDelegate.invokeTask (zone.js?fad3:425)
    at Object.onInvokeTask (core.js?223c:4620)
    at ZoneDelegate.invokeTask (zone.js?fad3:424)
    at Zone.runTask (zone.js?fad3:192)
    at ZoneTask.invokeTask [as invoke] (zone.js?fad3:499)
    at invokeTask (zone.js?fad3:1540)
    at HTMLFormElement.globalZoneAwareCallback (zone.js?fad3:1566)

Since this mentions the html, I will provide the html code aswell:

<form class="custom_form" #requestNewPasswordForm="ngForm" (ngSubmit)="requestNewPassword(requestNewPasswordForm)">
    <label>E-mail</label>
    <input class="custom_input" type="email" class="inputfield form-control" ngModel name="email">
    <button class="btn btn-default" type="submit">
      Send
    </button>
</form>
like image 788
DennisN Avatar asked Dec 18 '17 13:12

DennisN


2 Answers

After a lot a lot of effort, I have managed to locate and fix the problem. The problem was that I have an authorization interceptor that is intercepting every request to add an Authorization header with a user access token.

Since the call I was trying to do didn't require a user access token, but an application access token (authenticate as application for public Http requests like register, forgot password etc.), I decided to chain the call to get the application access token and the call for the forgotten password and just pass the retrieved application access token to the forgotten password method in my service and set it in the Authorization header there. This code was all fine. The problem was that I had edited the interceptor to check wether there was an Authorization header present and if so do nothing and THAT was the cause of the bug.

Instead of doing nothing, I should have just returned the request, so it just gets executed without modifications to the header.

so instead of

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if(request.headers.get('Authorization') == null) {
          //Code to add Authorization header
          return next.handle(requestWithAuthHeader)
        }
}

I had to do the following:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if(request.headers.get('Authorization') == null) {
          //Code to add Authorization header
          return next.handle(requestWithAuthHeader)
        } else {
          return next.handle(request);
        }
}

Otherwise the request gets intercepted and never executes because it doesn't get returned by the interceptor. The method in my component is subscribed to the result of the service method and thus expects an observable, but nothing ever gets returned because the request got intercepted and the interceptor noticed that an Authorization header was already present (set in the service) so decided to do nothing with the request.

This explains why I got the error stating that I had provided undefined while a stream (observable) was expected on the subscribe line in my component's method.

like image 196
DennisN Avatar answered Nov 16 '22 07:11

DennisN


I had a very similar issue. It also ended up being a problem with an HTTP interceptor. I had an automatic deserialization function running on all responses with a particular API endpoint pattern. Responses which weren't instances of HTTPResponse ended up being eaten. Adding an else statement which returned the response untouched when not an instance of HTTPResponse solve the issue for me:

@Injectable()
export class DeserializerInterceptor implements HttpInterceptor {
  public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).map(
      event => {
        if (event instanceof HttpResponse) {
          let response = event as HttpResponse<any>;
          // ... deserialization of response object
          return response;
        } else {
          return event; // ... adding this else statement fixed the `undefined` error.
        }
      }
    );
  }
}

(Removed error handling code, for clarity.)

like image 38
André C. Andersen Avatar answered Nov 16 '22 06:11

André C. Andersen