angular: Host should not return a redirect source file from `getSourceFile`

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

When using webpack-dev-server in --watch mode and you make a change the build process throws the following error:

ERROR in Error: Debug Failure. False expression: Host should not return a redirect source file from `getSourceFile`
    at tryReuseStructureFromOldProgram (C:\Users\me\repo\web-app-angular\node_modules\typescript\lib\typescript.js:74285:26)
    at Object.createProgram (C:\Users\me\repo\web-app-angular\node_modules\typescript\lib\typescript.js:73988:34)
    at AngularCompilerProgram._updateProgramWithTypeCheckStubs (C:\Users\me\repo\web-app-angular\packages\compiler-cli\src\transformers\program.ts:508:26)
    at C:\Users\me\repo\web-app-angular\packages\compiler-cli\src\transformers\program.ts:187:18
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

Expected behavior

It should not throw an error and compile the code as usual.

Minimal reproduction of the problem with instructions

See this PR for more info: https://github.com/dherges/ng-packagr/pull/637

What is the motivation / use case for changing the behavior?

Webback watch mode is useless if you have to restart it after each change.

Environment


Angular version: 5.2.6 | 6.0.0-beta.6

 
For Tooling issues:
- Node version: 8.9.4
- Platform:  Windows

Others:

Typescript: 2.6.2
Webpack: 4.0.1
Webpack-dev-server: 3.1.0

Conclusion

This seems to work. Not sure what else it will break though. Added the redirectInfo fix from the PR i referenced.

// program.js:485 (compiled file)
tmpProgram.getSourceFiles().forEach(function (sf) {
    if (_this.hostAdapter.isSourceFile(sf.fileName)) {
        if (sf['redirectInfo']) {
            sf = sf['redirectInfo'].redirectTarget;
        }
        sourceFiles.push(sf.fileName);
    }
    if (util_1.TS.test(sf.fileName) && !util_1.DTS.test(sf.fileName)) {
        tsFiles.push(sf.fileName);
    }
});
// program.ts:568 (source file)
// note: redirectInfo does not exists on ts.SourceFile hence the any.
tmpProgram.getSourceFiles().forEach(sf => {
  if (this.hostAdapter.isSourceFile(sf.fileName)) {
      if ((sf as any)['redirectInfo']) {
          sf = (sf as any)['redirectInfo'].redirectTarget;
      }
    sourceFiles.push(sf.fileName);
  }
  if (TS.test(sf.fileName) && !DTS.test(sf.fileName)) {
    tsFiles.push(sf.fileName);
  }
});
console.log(sf.fileName);
console.log(sf['redirectInfo'].redirectTarget.fileName);
--------------------------
C:/Users/me/repo/web-app-angular/node_modules/@types/webpack/node_modules/source-map/source-map.d.ts
C:/Users/me/repo/web-app-angular/node_modules/@types/uglify-js/node_modules/source-map/source-map.d.ts
--------------------------

Edit: It seems like my ticket is not clear enough in pointing out the issue based on the new posts in this thread. The issue happens due to identical *.d.ts file found via two paths in the compilation. In plain english: The packages uses the same dependency that includes a definition file.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 59
  • Comments: 55 (11 by maintainers)

Commits related to this issue

Most upvoted comments

@alexeagle Will this ever be looked at? It’s easily reproducible, seems to be a simple fix and affects a lot of users based on the number of “Thumbs up”. The error still occurs in Angular 8 RC.

After updating @angular to 7.0.0 and ng-packagr to 4.3.1 I have this error as well every time I change something in code during serve --aot mode: ERROR in Debug Failure. False expression: Host should not return a redirect source file from 'getSourceFile'

@charlieargue

Tested with Typescript 2.7.2:

Open node_modules/typescript/lib/typescript.js (the compiled source in your project)

Search for “Host should not return a redirect source file from getSourceFile” In TS 2.7.2 that is around line 27286.

Directly before this line (the assert) insert:

if (newSourceFile.redirectInfo) {
  console.warn('Evil file:', newSourceFile.redirectInfo.redirectTarget.fileName);
}

Start AOT dev build, change sth, watch the error.

Just wanted to let you know that the PR for this issue is done. We’re waiting for the code owner to be back and then approve. So hopefully in a short time we just leave this thread behind us!

I’ve asked the team to consider re-triaging this issue and raising the priority after reading the ngx-bootstrap post on upgrading to Ivy.

I was finally able to find the root cause of the problem (for my use case at least). This bug shows its ugly head whenever there a duplicate typings in the project.

In my case it reappeared when upgrading to redux 4.0.0 in a project, and some plugins still have a dependency on 3.x.x. So I ended up with

node_modules/redux
node_modules/@types/redux-logger/node_modules/redux 

and redux brings it’s own typings … twice in my case.

removing the nested node_modules caused the incremental build to finally run on TS 2.7.

How did I find the culprits? I hacked the installed typescript compiler and expanded the error message to also show the file that caused the error, not only the “source redirect” without additional info.

Basically just put a if(newSourceFile.redirectInfo)-wrapped log before the compiled source at

https://github.com/Microsoft/TypeScript/blob/fbeb58a147962aa31a4ef2b00bc4e778849c1ff0/src/compiler/program.ts#L1031

and then the bad files show up. At least that worked in my case.

As a workaround I added a postinstall script to package.json that removes the folders, like

https://github.com/DcsMarcRemolt/ngx-starter-new/blob/master/package.json#L22

Oh and a colleague also had a node_modules folder ABOVE the project folder also containing duplicate typings. Removing that finally fixed it on his machine too.

This is also a problem in Angular 8 when using ivy. From: @angular\compiler-cli\src\ngtsc\typecheck\src\context.js:164-173

for (var _c = tslib_1.__values(originalProgram.getSourceFiles()), _d = _c.next(); !_d.done; _d = _c.next()) {
    var originalSf = _d.value;
    var sf = this.transform(originalSf);
    
    sfMap.set(sf.fileName, sf);
    if (!sf.isDeclarationFile && this.opMap.has(originalSf)) {
        interestingFiles.push(sf);
    }
}

Fixed in the same way by adding if (sf['redirectInfo']) { sf = sf['redirectInfo'].redirectTarget; }

for (var _c = tslib_1.__values(originalProgram.getSourceFiles()), _d = _c.next(); !_d.done; _d = _c.next()) {
    var originalSf = _d.value;
    var sf = this.transform(originalSf);
    if (sf['redirectInfo']) { sf = sf['redirectInfo'].redirectTarget; }
    sfMap.set(sf.fileName, sf);
    if (!sf.isDeclarationFile && this.opMap.has(originalSf)) {
        interestingFiles.push(sf);
    }
}

this still happen in cli v6 with aot: true in “serve” target.

Thanks to this great pointer from @DcsMarcRemolt, I was able to find our offending code that lead to this error.

In node_modules/typescript/lib/typescript.js, I changed line:

ts.Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`")

to:

 ts.Debug.assert(
    !newSourceFile.redirectInfo, 
    "Host should not return a redirect source file from `getSourceFile`", 
    () => `Duplicate .d.ts files: ${newSourceFile.fileName} <-> ${newSourceFile.redirectInfo.redirectTarget.fileName}`
);

And then got the error like:

ERROR in Debug Failure. False expression: Host should not return a redirect source file from `getSourceFile`
Verbose Debug Information: Duplicate .d.ts files: C:/<...>/node_modules/ngx-bootstrap/Typeahead/ngx-bootstrap-typeahead.d.ts <-> C:/<...>/node_modules/ngx-bootstrap/typeahead/ngx-bootstrap-typeahead.d.ts

Which I found odd, so I searched our code base for: /Typeahead with a case-sensitive flag, and, lo and behold, someone added an import like import { TypeaheadMatch } from 'ngx-bootstrap/Typeahead';. After fixing the letter casing, the error was gone.

A bit unfortunate that such a small typo can cause such an obscure error that lead to hours of debugging, but, oh well… it happens. Hope this helps someone else!

I’ve noticed that using the paths-workaround is resulting in a bad side effect in our monorepo. Since Typescript added support for definitions source maps so we can CTRL-click our way around the packages in our monorepo, but the paths-workaround makes us end up in the node_modules folder instead of the project source folder. If I instead of using the old way

"paths": {
      "@my-project/*": [ "node_modules/@my-project/*"]
}

uses this way of pointing to our src folders instead I end up and the correct file when CTRL+click

"paths": {
      "@my-project/*": [ "../*"]
}

however when I import stuff from another project instead of adding import ... from '@my-project/package' it adds import ... from '../package' which is not what I want.

So the only way of fixing this issue and keep all the nice features (auto import/ctrl+click to other packages in our monorepo) is to monkey patch angular, which I just did. I’ve added the same fix as in my PR. It’s pretty robust since it looks for a specific line of code. So it won’t have to be updated unless the Angular-team changes that specific line.

To use this. Just add it as a postinstall script in package.json.

  "scripts": {
    "postinstall": "node path/patch-cli"
  }
/*
Monkey Patch for issue: https://github.com/angular/angular/issues/22524

Error message:
ERROR in Error: Debug Failure. False expression: Host should not return a redirect source file from `getSourceFile`
*/

const { readFileSync, writeFileSync, existsSync } = require('fs');
const fileName = './node_modules/@angular/compiler-cli/src/transformers/program.js';
const patchIdentifier = '//-patched';
const sourceCode = 'if (this.hostAdapter.isSourceFile(sf.fileName)) {';
const patchCode = "if (sf['redirectInfo']) { sf = sf['redirectInfo'].redirectTarget; } " + patchIdentifier; //eslint-disable-line quotes

// No file, nothing to patch
if (!existsSync(fileName)) {
    process.exit(0);
}

const contents = readFileSync(fileName).toString().split('\n');
// Check if code has been patched already
const hasBeenPatched = contents.find(line => line.indexOf(patchIdentifier) !== -1); // eslint-disable-line strict

if (!hasBeenPatched) {
    const lineNumber = contents.findIndex(line => line.indexOf(sourceCode) !== -1); // eslint-disable-line quotes, strict
    if (lineNumber <= 0) {
        console.error('Could not find source code. Please check ' + fileName + ' and update the patch accordingly'); // eslint-disable-line no-console
        process.exit(1);
    }
    // Add the patched line after the source code line
    contents.splice(lineNumber + 1, 0, patchCode);
    const updatedContents = contents.join('\n');

    writeFileSync(fileName, updatedContents);

    console.log('Angular Compiler CLI has been Monkey patched'); // eslint-disable-line no-console
} else {
    console.log('Angular Compiler CLI has already been patched'); // eslint-disable-line no-console
}

process.exit(0);

I really despise that I have to do this. But the Angular-team leaves me no other choice.

Edit: In the lastest Angular they’ve changed the TS output to ES6. So instead of using _this.hostAdapter you need to use this.hostAdapter when searching for the line number

Edit 2: They switched back again in Angular 8.

After updating @angular to 7.0.0 and ng-packagr to 4.3.1 I have this error as well every time I change something in code during serve --aot mode: ERROR in Debug Failure. False expression: Host should not return a redirect source file from 'getSourceFile'

Try change tsconfig.json

"compilerOptions": {
...
   "paths": {
      "any-symlink-package": ["real-path-of-any-symlink-package"],
      "any-symlink-package/*": ["real-path-of-any-symlink-package/*"],
      ...
      "*": [ "node_modules/*", "node_modules/@types/*"]
   }
}

It works for me eventually. @piernik

I started digging a little bit again since this Angular 6/7 is on my plate again. I logged all the files from tmpProgram.getSourceFiles(), and it seems like it even includes files explicitly exluded in the tsconfig.

// packages/web-app/tsconfig.json
"exclude": [
    "../web-lib-angular/node_modules"
  ]
// log
...
C:/Users/me/repo/web/packages/web-app/node_modules/tslib/tslib.d.ts
C:/Users/me/repo/web/packages/web-lib-angular/node_modules/tslib/tslib.d.ts
...

The files above will cause the error.

I also found an interesting workaround. This makes sure that any tslib used will be the same one.

"paths": {
      "tslib": [ "node_modules/tslib"]
}

my god man – this work around saved my ass – thank you. I’ve been having an issue with bringing in an ngpackagr library and for some reason rxjs library also points to Rxjs (case change) and it made the compiler for typescript go crazy on rebuilds. Added the following to tsconfig:

"paths": {
      "rxjs": ["../node_modules/rxjs"],
      "Rxjs": ["../node_modules/rxjs"]
    }

ugh. thank you again.

This is actually not a new problem, see angular/angular-cli/issues/8332

Wish they would have taken it seriously back then, now we have the mess as TS 2.6 is supported.

@alexeagle Based on the number of thumbs up in the OP and the activity in this issue, do you still consider this as Frequency low? You are aware that we are unable to compile Angular at all with this bug, right?

Also, I created a PR for this 6 months a go: https://github.com/angular/angular/pull/26036

I started digging a little bit again since this Angular 6/7 is on my plate again. I logged all the files from tmpProgram.getSourceFiles(), and it seems like it even includes files explicitly exluded in the tsconfig.

// packages/web-app/tsconfig.json
"exclude": [
    "../web-lib-angular/node_modules"
  ]
// log
...
C:/Users/me/repo/web/packages/web-app/node_modules/tslib/tslib.d.ts
C:/Users/me/repo/web/packages/web-lib-angular/node_modules/tslib/tslib.d.ts
...

The files above will cause the error.

I also found an interesting workaround. This makes sure that any tslib used will be the same one.

"paths": {
      "tslib": [ "node_modules/tslib"]
}

@DcsMarcRemolt Sorry that you had to spend additional time on this. I thought the root cause was clear in my original post since I posted a solution for it, and the PR I linked to states “Issue happens due to identical *.d.ts file found via two paths in the compilation.” I’ve updated my post with this to avoid any future confusion.

Short answer to your initial question: no. No backwards updates. The timeline clearly says we move forward. Breaking changes every major, etc. Basic semver.

If you’re stuck in v6 and you can’t upgrade “cause a day”, I really feel for you and your team. Someone needs to tell management or the PO that this will save you days when 9 comes out. Or weeks when 10 comes out.

GL HF. Let me know if you want external help, I’ll be happy to accommodate you.

@Bjeaurn Look at Mr Fancypants over here with the spare day to spend fixing compile errors for a framework upgrade! 😉

Thanks guys! Just wanted to confirm this workaround is 💯 👍 – – and yes, had to do something like this:

"hackscript": "rimraf node_modules/@types/uglify-js/node_modules",

Once I found the culprit module – and pretty much keeps coming back, so the package.json script is necessary.

Changing the webpack configs to JS was not sufficient in my case. But as soon as I removed the [at]types/webpack and [at]types/webpack-merge packages from my project the bug disappeared. Finally a current TS version for my projects!

My conflicting packages were @types/webpack": "^4.4.5" and @types/webpack-env": "1.13.0". The solution was to load webpack for my webpack-config files and webpack-env for my source code - via the tsconfig.json types option.

@soates only a workaround as far as I know. You need to determine what module imports a duplicate .d.ts file and delete that duplicate dependency from the module. For example using a npm postinstall script. In my case it was fontawesome packages that all required a common types module with the types.

Remember to vote on the original post so that it gets visibility!

Confirming that after upgrading to Angular 6, still have the same ERROR. Thanks again for any guidance on this!

Error

ℹ 「wdm」: Compiling...
✖ 「wdm」:    1034 modules

ERROR in Debug Failure. False expression: Host should not return a redirect source file from `getSourceFile`
ℹ 「wdm」: Failed to compile.

relevant package.json:

...
    "start": "rimraf dist && webpack-dev-server --progress --info --config config/webpack.localhost.ts --hot --bail",
...
"dependencies": {
    "@angular/animations": "^6.0.3",
    "@angular/common": "^6.0.3",
    "@angular/compiler": "^6.0.3",
    "@angular/core": "^6.0.3",
    "@angular/forms": "^6.0.3",
    "@angular/http": "^6.0.3",
    "@angular/platform-browser": "^6.0.3",
    "@angular/platform-browser-dynamic": "^6.0.3",
    "@angular/router": "^6.0.3",
    "@ng-bootstrap/ng-bootstrap": "2.0.0",
    "@ngx-loading-bar/core": "^2.0.0",
    "@types/angular": "^1.6.45",
    "@types/lodash": "4.14.109",
    "@types/socket.io-client": "1.4.32",
    "angular-confirmation-popover": "4.1.0",
    "angular2-json2csv": "1.1.2",
    "angular2-moment": "1.9.0",
    "angular2-template-loader": "0.6.2",
    "angular2-toaster": "6.0.0",
    "angular2-uuid": "1.1.1",
    "animate.css": "3.6.1",
    "bootstrap": "4.1.1",
    "core-js": "^2.5.7",
    "font-awesome": "4.7.0",
    "glyphicons-halflings": "1.9.1",
    "hammerjs": "2.0.8",
    "http-server": "0.11.1",
    "lodash": "4.17.10",
    "moment": "2.22.1",
    "ng2-file-upload": "1.3.0",
    "ng2-img-cropper": "0.9.0",
    "ng2-scroll-to": "1.0.7",
    "node-sass": "4.8.3",
    "raven-js": "3.25.2",
    "reflect-metadata": "0.1.12",
    "rxjs": "^6.2.0",
    "socket.io-client": "2.1.1",
    "typings": "2.1.1",
    "zone.js": "^0.8.26"
  },
  "devDependencies": {
    "@angular/compiler-cli": "^6.0.3",
    "@ngtools/webpack": "^6.1.0-beta.1",
    "@types/angular": "^1.6.45",
    "@types/chai": "4.1.3",
    "@types/core-js": "^0.9.46",
    "@types/hammerjs": "2.0.35",
    "@types/node": "^10.1.4",
    "@types/rx": "^4.1.1",
    "@types/socket.io-client": "1.4.32",
    "@types/webpack": "^4.4.0",
    "angular2-template-loader": "0.6.2",
    "awesome-typescript-loader": "5.0.0",
    "copy-webpack-plugin": "^4.5.1",
    "css-loader": "^0.28.11",
    "extract-text-webpack-plugin": "4.0.0-beta.0",
    "file-loader": "^1.1.11",
    "html-loader": "0.5.5",
    "html-webpack-plugin": "3.2.0",
    "jasmine-core": "3.1.0",
    "jasmine-spec-reporter": "4.2.1",
    "karma": "2.0.2",
    "karma-chrome-launcher": "2.2.0",
    "karma-jasmine": "1.1.2",
    "karma-phantomjs-launcher": "1.0.4",
    "karma-sourcemap-loader": "0.3.7",
    "karma-verbose-reporter": "0.0.6",
    "karma-webpack": "3.0.0",
    "live-server": "^1.2.0",
    "lodash": "4.17.10",
    "moment": "2.22.1",
    "ngc-webpack": "^4.1.2",
    "node-sass": "^4.9.0",
    "null-loader": "0.1.1",
    "phantomjs-prebuilt": "2.1.16",
    "protractor": "5.3.2",
    "raw-loader": "^0.5.1",
    "request": "2.87.0",
    "rimraf": "^2.6.2",
    "sass-loader": "^7.0.1",
    "style-loader": "^0.21.0",
    "supertest": "3.1.0",
    "to-string-loader": "^1.1.5",
    "ts-loader": "^4.3.0",
    "ts-node": "^6.0.5",
    "tslint": "^5.10.0",
    "tslint-loader": "^3.6.0",
    "typescript": "^2.7.2",
    "typings": "2.1.1",
    "url-loader": "1.0.1",
    "webpack": "^4.10.2",
    "webpack-bundle-analyzer": "^2.13.1",
    "webpack-cli": "^2.1.4",
    "webpack-dev-server": "3.1.4",
    "webpack-merge": "^4.1.2",
    "write-file-webpack-plugin": "4.3.2"
  },
  "repository": {}
}