It doesn't sound like you want a mandatory parameter at all. Making it mandatory will require input which makes the default value useless. However, let's get back to the error your described.
The attribute works as expected. The problem is how you're calling your script. Lets try the following code as a function and a script:
[CmdLetBinding()]
param(
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
[string]$User = 't'
)
"`$User has value '$user'. Is it null? $($user -eq $null). Type is $($user.GetType().Name)"
Demo:
#Inside a function
PS> t -User ""
$User has value ''. Is it null? False. Type is String
#Inside a script
PS> .\Untitled-5.ps1 -User ""
$User has value ''. Is it null? False. Type is String
#Running the following command from cmd
cmd> powershell.exe -file Untitled-5.ps1 -User ""
$User has value ''. Is it null? False. Type is String
However, when you run the last line in a PowerShell-session, then PowerShell will parse the string which results in an empty value (no quotes) for the parameter.
PS> powershell.exe -file Untitled-5.ps1 -User ""
D:\Downloads\Untitled-5.ps1 : Missing an argument for parameter 'User'. Specify a parameter of type 'System.String' and
try again.
+ CategoryInfo : InvalidArgument: (:) [Untitled-5.ps1], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument,Untitled-5.ps1
Powershell.exe is not intended to be used inside a PowerShell-process. You can fix this by calling scripts directly inside a PowerShell-process (second example), run PowerShell.exe … from cmd/run/scheduled task ++ (anywhere else) or by making sure PS doesn't parse your arguments so it will actually input the quotes.
This can be done by escaping the quotes
PS> powershell -File Untitled-5.ps1 -User `"`"
Or by using --%
PS> powershell -File Untitled-5.ps1 --% -User ""
cmd.exe. (Why?) Run it from a PowerShell prompt.