Debugging failing SSRS report caused by read-only report parameter

I recently experienced problems when trying to view a report for SQL Server Reporting Services with ReportViewer for MVC.

 

The report worked in some cases, but failed when certain parameters were set.

Exception and stack trace:

[ReportServerException: The report parameter 'DeviceTypes' is read-only and cannot be modified. (rsReadOnlyReportParameter)]
Microsoft.Reporting.WebForms.ServerReportSoapProxy.OnSoapException(SoapException e) +155
Microsoft.Reporting.WebForms.Internal.Soap.ReportingServices2005.Execution.ProxyMethodInvocation.Execute(RSExecutionConnection connection, ProxyMethod`1 initialMethod, ProxyMethod`1 retryMethod) +1256
Microsoft.Reporting.WebForms.Internal.Soap.ReportingServices2005.Execution.RSExecutionConnection.SetExecutionParameters(ParameterValue[] Parameters, String ParameterLanguage) +370
Microsoft.Reporting.WebForms.SoapReportExecutionService.SetExecutionParameters(IEnumerable`1 parameters, String parameterLanguage) +530
Microsoft.Reporting.WebForms.ServerReport.SetParameters(IEnumerable`1 parameters) +896
Microsoft.Reporting.WebForms.Report.SetParameters(ReportParameter parameter) +136
ReportViewerForMvc.ReportExtensions.SetParameters(Report report, ReportParameterInfoCollection collection) +114
ReportViewerForMvc.ReportViewerExtensions.SetProperties(ServerReport serverReport, ServerReport properties) +84
ReportViewerForMvc.ReportViewerExtensions.SetProperties(ReportViewer reportViewer, ReportViewer properties) +117
ReportViewerForMvc.ReportViewerWebForm.BuildReportViewer() +92
System.Web.UI.Control.OnLoad(EventArgs e) +109
System.Web.UI.Control.LoadRecursive() +68
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4498

 

Checked the ReportServerService__*.log and ExecutionLog* database views, but they contained no additional clues about the error.

 

I attached WinDbg (x64) to the IIS worker process (w3wp.exe) and set a breakpoint with:

!bpmd   ReportViewerForMvc.dll   ReportViewerForMvc.ReportExtensions.SetParameters

Messages from WinDbg:

Found 2 methods in module 00007ff9bde79bb0...
MethodDesc = 00007ff9c00a30d0
MethodDesc = 00007ff9c00a30e0
Setting breakpoint: bp 00007FF9C0EE5590 [ReportViewerForMvc.ReportExtensions.SetParameters(Microsoft.Reporting.WebForms.Report, Microsoft.Reporting.WebForms.ReportParameterInfo)]
Setting breakpoint: bp 00007FF9C0EE53E4 [ReportViewerForMvc.ReportExtensions.SetParameters(Microsoft.Reporting.WebForms.Report, Microsoft.Reporting.WebForms.ReportParameterInfoCollection)]
Adding pending breakpoints...

 

Continued exection, reloaded the report and the breakpoint was hit.

Checked the .NET call stack with:

!clrstack -a

Part of the result:

OS Thread Id: 0x273c (45)
Child SP               IP Call Site
00000007dba9d030 00007ff9c0ee53e4 ReportViewerForMvc.ReportExtensions.SetParameters(Microsoft.Reporting.WebForms.Report, Microsoft.Reporting.WebForms.ReportParameterInfoCollection)
PARAMETERS:
report (<CLR reg>) = 0x0000000390e93650
collection (<CLR reg>) = 0x0000000490886678

 

Examined the collection object with:

!DumpObj /d 0000000490886678

Result:

Name:        Microsoft.Reporting.WebForms.ReportParameterInfoCollection
MethodTable: 00007ff9bff1cc98
EEClass:     00007ff9bff08d40
Size:        32(0x20) bytes
File:        C:\Windows\assembly\GAC_MSIL\Microsoft.ReportViewer.WebForms\11.0.0.0__89845dcd8080cc91\Microsoft.ReportViewer.WebForms.dll
Fields:
MT    Field   Offset                 Type VT     Attr            Value Name
00007ffa1b23d408  40017fa        8 ...Canon, mscorlib]]  0 instance 00000004908823f0 list
00007ffa1b8de068  40017fb       10        System.Object  0 instance 0000000000000000 _syncRoot

 

Examined the list array with:

!DumpArray /d 00000004908823f0

Result:

Name:        Microsoft.Reporting.WebForms.ReportParameterInfo[]
MethodTable: 00007ff9c0fd9e60
EEClass:     00007ffa1b2f0f10
Size:        120(0x78) bytes
Array:       Rank 1, Number of elements 12, Type CLASS
Element Methodtable: 00007ff9bff1cbc0
[0] 00000004908826c8
[1] 00000004908829d8
[2] 0000000490882ce8
[3] 00000004908832e0
[4] 0000000490884490
[5] 00000004908847a8
[6] 0000000490884ac0
[7] 0000000490884e38
[8] 00000004908853f8
[9] 0000000490885818
[10] 0000000490886250
[11] 00000004908865c8

 

Examined details for the list array with:

!DumpArray /d -details 00000004908823f0

Part of the result:

[11] 00000004908865c8
Name:        Microsoft.Reporting.WebForms.ReportParameterInfo
MethodTable: 00007ff9bff1cbc0
EEClass:     00007ff9bff08cc8
Size:        104(0x68) bytes
File:        C:\Windows\assembly\GAC_MSIL\Microsoft.ReportViewer.WebForms\11.0.0.0__89845dcd8080cc91\Microsoft.ReportViewer.WebForms.dll
Fields:
MT    Field   Offset                 Type VT     Attr            Value Name
00007ffa1b8dda88  4000120        8            System.String      0     instance     0000000490881950     m_name
00007ff9bff1c678  4000121       50             System.Int32      1     instance                    4     m_dataType
00007ffa1b8cd6f8  4000122       58           System.Boolean      1     instance                    0     m_isNullable
00007ffa1b8cd6f8  4000123       59           System.Boolean      1     instance                    1     m_allowBlank
00007ffa1b8cd6f8  4000124       5a           System.Boolean      1     instance                    1     m_isMultiValue
00007ffa1b8cd6f8  4000125       5b           System.Boolean      1     instance                    1     m_isQueryParameter
00007ffa1b8dda88  4000126       10            System.String      0     instance     000000038e411420     m_prompt
00007ffa1b8cd6f8  4000127       5c           System.Boolean      1     instance                    0     m_promptUser
00007ffa1b8cd6f8  4000128       5d           System.Boolean      1     instance                    1     m_areDefaultValuesQueryBased
00007ffa1b8cd6f8  4000129       5e           System.Boolean      1     instance                    1     m_areValidValuesQueryBased
00007ffa1b8dda88  400012a       18            System.String      0     instance     0000000000000000     m_errorMessage
00007ff9c0fda240  400012b       20     ...Viewer.WebForms]]      0     instance     0000000490886320     m_validValues
00007ffa1b23d128  400012c       28     ...tring, mscorlib]]      0     instance     0000000490886658     m_currentValues
00007ff9bff1c7c8  400012d       54             System.Int32      1     instance                    0     m_state
00007ff9bff1cc98  400012e       30     ...terInfoCollection      0     instance     0000000490886fb8     m_dependencyCollection
00007ff9bff1cc98  400012f       38     ...terInfoCollection      0     instance     0000000000000000     m_dependentsCollection
00007ffa1b8c1480  4000130       40          System.String[]      0     instance     0000000490881bb8     m_dependencies
00007ff9c0fda5d0  4000131       48     ...Viewer.WebForms]]      0     instance     0000000490886630     m_dependentsCollectionConstruction
00007ffa1b8cd6f8  4000132       5f           System.Boolean      1     instance                    1     m_visible

 

Examined the name of the last report parameter with:

!DumpObj /d 0000000490881950

Part of the result:

Name:        System.String
MethodTable: 00007ffa1b8dda88
EEClass:     00007ffa1b236a08
Size:        48(0x30) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      DeviceTypes

So the last parameter was DeviceTypes, the one causing the problem.

 

Looked for a read-only property among the report parameters. Noticed that m_promptUser was 0 for the DeviceTypes parameter, while m_promptUser was 1 for all other parameters.

 

I decided to use procdump to log exception messages from the ReportingServicesService.exe process with:

procdump.exe -f "" -l -e 1 ReportingServicesService.exe

 

Reloaded the report to recreate the error.

Part of the result was:

[07:10:37] Exception: E0434F4D.Microsoft.ReportingServices.Diagnostics.Utilities.ReadOnlyReportParameterException ("The report parameter 'DeviceTypes' is read-only and cannot be modified.")
[07:10:37] Exception: E0434F4D.Microsoft.ReportingServices.Diagnostics.Utilities.RSException ("The report parameter 'DeviceTypes' is read-only and cannot be modified.")

 

Then loaded all DLL files from SSRS into ILSpy from:

C:\Program Files\Microsoft SQL Server\MSRS11.MSSQLSERVER\Reporting Services\ReportServer\bin

 

Searched for the ReadOnlyReportParameterException class in ILSpy.

Found it and noticed that it was apparently only used by:

Microsoft.ReportingServices.ReportProcessing.ParameterInfoCollection.Combine(...)

 

Read the decompiled code for the above method and noticed this part:

if (checkReadOnly && !parameterInfo.PromptUser)
{
  ParameterInfoCollection.ThrowReadOnlyParameterException(parameterInfo.Name, isSharedDataSetParameter);
}

Apparently the value of parameterInfo.PromptUser determined the read-only status.

 

I decided to examine the XML for the RDL report and noticed that the DeviceTypes report parameter did not have a Prompt section.

I added this:

<Prompt>DeviceTypes</Prompt>

Then uploaded the new version of the report (by overwriting).

However this did not seem to have any immediate effect, the error still occured…

 

I spent some time examining various other ideas and internals of SSRS.

 

Then I got an idea: I decided to delete the problematic report and then recreate the modified version with a Prompt section.

This fixed the problem! The report now worked in all cases, no matter which parameters were set.

This behavior was consistent across 2 SSRS instances.

Conclusion

It seems that read-only status for a report parameter is derived from the presence of a <Prompt>Description</Prompt> section.

However this status is not updated if the report is overwritten, it has to be deleted and then recreated…

 

Due to the way ReportViewerForMvc.dll is implemented all report parameters are copied from the initial instance to another instance of ReportViewer.

This means that report parameters can’t be read-only, so <Prompt>Description</Prompt> should always be specified for all report parameters, when using ReportViewer for MVC.