Skip to content

Import-Module does not properly report errors to users #9855

Closed

Description

@KirkMunro

If you have a script module that generates a terminating error, the error message shown to users does not indicate that the error came from Import-Module. Instead it simply shows the script module internals. This is inappropriate for users who may not have authored that script module.

If you have a function in a script module that generates a terminating error, and you invoke that function when the module is not loaded, the module will load, the terminating error will not be shown to end users at all, and the function will run. The suppression of the terminating error in this scenario needs to be corrected so that users see the error came from Import-Module which was implicitly invoked by invoking the command while auto-loading is enabled.

Note: This issue is closely related to the problem with terminating error handling in PowerShell (for which a collection of RFCs have been submitted as a PR). Terminating error handling aside, this issue focuses on how Import-Module does a poor job of reporting errors, sometimes not even reporting them at all.

Steps to reproduce

$moduleName = 'ModuleErrorHandlingTest'
$modulePath = Join-Path -Path $([Environment]::GetFolderPath('MyDocuments')) -ChildPath PowerShell/Modules/${moduleName}
New-Item -Path $modulePath -ItemType Directory -Force > $null
$nmmParameters = @{
    Path = "${modulePath}/${moduleName}.psd1"
    RootModule = "./${moduleName}.psm1"
    FunctionsToExport = @('Test-ErrorHandling')
}
New-ModuleManifest @nmmParameters
$scriptModulePath = Join-Path -Path $modulePath -ChildPath "${moduleName}.psm1"
New-Item -Path $scriptModulePath -ItemType File | Set-Content -Encoding UTF8 -Value @'
    1/0 # Oops!
    function Test-ErrorHandling {
        [cmdletBinding()]
        param()
        'Error handling output'
    }
'@

# This next command generates an error while loading the module, but the module still
# loads, and the error message does not tell the user that it came from Import-Module
# nor does it tell the user which module was being loaded when the error occurred.
Import-Module $moduleName
Remove-Module $moduleName

# This next command generates an error while implicitly loading a module, but the error
# is silenced and the module loads anyway so the end user has no idea that something
# happened.
Test-ErrorHandling
Remove-Module $moduleName
Remove-Item -LiteralPath $modulePath -Recurse -Force

Expected behavior

Note the differences in the actual error text that are shown in what should be expected when a module is loaded over what is actually shown below this code block.

Import-Module: Unexpected error while importing ModuleErrorHandlingTest module. Attempted to divide by zero.
At line:23 char:1
+ Import-Module $moduleName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

Import-Module: Unexpected error while implicitly importing ModuleErrorHandlingTest module. Attempted to divide by zero.
At line:29 char:1
+ Test-ErrorHandling
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

Error handling output

Actual behavior

Attempted to divide by zero.
At C:\Users\kirka\OneDrive\Documents\PowerShell\Modules\ModuleErrorHandlingTest\ModuleErrorHandlingTest.psm1:1 char:5
+     1/0 # Oops!
+     ~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

Error handling output

Environment data

Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Activity

  1. added
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    on Jun 7, 2019
  2. bpayette commented on Jun 10, 2019

    @bpayette
    Contributor

    Kirk Munro (@KirkMunro)

    So you are suggesting that Import-Module should catch all exceptions and wrap them in a PSModuleImportErrorException ?

    Note: IMHO the module author should be dealing explicitly with errors and providing an appropriate experience for the end user rather than simply relying on the PowerShell runtime to take care of error handling and presentation.

  3. KirkMunro commented on Jun 10, 2019

    @KirkMunro
    ContributorAuthor

    Bruce Payette (@bpayette) Something like that.

    1. I think module authors should deal with all errors (but they don't necessarily do that because they aren't forced to do so).
    2. I think users should not see internals from modules that they likely didn't create if Import-Module fails (but they do because of how it handles errors).
    3. I think the PowerShell runtime should not silence errors from dynamically loaded modules.

    In my mind this means two things:

    1. Providing better handling of errors in Import-Module so that...
      • ...unexpected errors from module loading clearly identify the source of the error (the loading of the module), allowing end users to decide what action to take (chuck the module because it's crap, contact the author, something else...).
      • ...errors that occur from implicit module loading are made visible to end users.
    2. Beyond making the source of errors more visible to end users, get momentum/acceptance and implementation on at least some of this collection of RFCs that are in a PR right now -- specifically on the one to do with terminating error handling, so that we can have terminating errors that actually terminate.

    My goal here isn't to help module authors be lazy. It's to help end users identify where problems actually come from so that they can decide an appropriate course of action if an error occurs during module load.

  4. iSazonov commented on Jun 11, 2019

    @iSazonov
    Collaborator

    Is the issue only for Import-Module, or other module cmdlets too, or module behavior in common?

  5. KirkMunro commented on Jun 11, 2019

    @KirkMunro
    ContributorAuthor

    I haven't stepped through the code while reproducing the error, but as far as I know this is only in Import-Module. The errors I have issue with occur while processing a psm1 file, which I don't believe is done anywhere outside of that cmdlet.

  6. fMichaleczek commented on Jun 11, 2019

    @fMichaleczek
    Contributor

    I'm agree. A little enhancement around module's error is a good thing !

  7. bpayette commented on Jun 12, 2019

    @bpayette
    Contributor

    Hey Kirk Munro (@KirkMunro)

    First - kudos for trying to take on this problem. It's bugged me for years.

    I think users should not see internals from modules that they likely didn't create if Import-Module fails (but they do because of how it handles errors).

    Hmm ... in your "expected output" example, you're still showing the line/line number in the module where the error occurred. I assume you intend that it should show the line where Import-Module was called (or using module or #requires for that matter)?

    WRT terminating errors, you can mostly get the behaviour you want simply by putting trap { break } at the top level of your module but what you can't do is create a RuntimeException and then set the InvocationInfo property so it points to where you want. If you trap/catch and then throw a new error it will report the throw line, not the original line. Having a way to "point" an error message to the appropriate location would make it possible for the script/module author to provide a much better error experience.

    Also note that overarching problem doesn't just apply to modules. Scripts and functions have exactly the same problem. A user calling a script shouldn't be exposed to the internals of the script (at least not by default)

    My goal here isn't to help module authors be lazy.

    I totally get that. A big part of the problem is that script/function/module authors can be "lazy" about error handling and get an end-user error experience. Not ideal certainly but "free". For compiled-cmdlet authors, error handling isn't free so they need to put some thought into it.

  8. KirkMunro commented on Jun 12, 2019

    @KirkMunro
    ContributorAuthor

    I assume you intend that it should show the line where Import-Module was called (or using module or #requires for that matter)?

    Correct. I created that output via a quick copy-paste-modify. It should show the line where the error occurred from the eyes of the end user (explicit import via Import-Module, using module, or #requires, or implicit import via invocation of a command that is in a module that has not been imported yet). I'll update the expected output in my post accordingly.

    Interesting point on how RuntimeException instances could be configured with a specific InvocationInfo. I'm stepping through some of the code related to this to see how it works, and will keep this idea in mind.

    Scripts and functions have exactly the same problem. A user calling a script shouldn't be exposed to the internals of the script (at least not by default)

    Bruce Payette (@bpayette): You mean scripts and functions that have the CmdletBinding attribute, not all scripts and functions, right? If so, I agree. 😉

    My line in the sand on this is CmdletBinding. If a script or function uses CmdletBinding, then the intent of that script or function is to be treated like a command, which is different than simple scripts or basic functions that are more often consumed by the author or a team, maybe shared as example code to be modified, but not shared as a finished product for end users to use as a black box. In scripts or functions that use CmdletBinding, I believe they should behave as much like a cmdlet as possible, not showing internals to people who invoke them.

  9. added
    Issue-Enhancementthe issue is more of a feature request than a bug
    and removed
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    on Oct 2, 2019
  10. metablaster commented on Dec 13, 2020

    @metablaster

    If your example module dot sources an external script that otherwise fails to be dot sourced then you will see no error.

    Import-Module won't report an error in this case, but if you manually dot source said script the error is shown.

    That's pretty much unexpected behavior which I encountered today so upvoting this.

  11. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  12. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  13. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  14. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or moreWG-Cmdletsgeneral cmdlet issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions