Am primit câteva jelanii vis-a-vis de niște rapoarte ce nu se generau, producând o eroare al draima de pâcloasă la prima vedere: System.ArgumentNullException: Value cannot be null. Vă rog, nu vă înghesuiți cu prea multe detalii că sunt un simplu om!

Foto: Bogdan Stoica
Nici stiva de funcții apelate (stack trace, pentru iubitorii de franceză) nu fu cu mult mai clarificatoare, cu atât mai puțin fiindcă problema-și avea originea într-unul din cotloanele umede și-ntunecate ale Telerik Report Engine. Orientativ:
2026-07-30 05:02:19.6717|ERROR|SmufReportGenerator|While generating Report|
Telerik.Reporting.Processing.CancelProcessingException:
An error occurred while evaluating the report parameters.
Report source cannot be processed.
Check the InnerException for more information.
---> System.ArgumentNullException: Value cannot be null.
Parameter name: element
at System.Attribute.GetCustomAttributes(MemberInfo element,
Type type,
Boolean inherit)
at System.Attribute.GetCustomAttribute(MemberInfo element,
Type attributeType,
Boolean inherit)
at Telerik.Reporting.Expressions.ExternalAssemblyFunctionLoader
.GetFunctionAttribute(MemberInfo info)
at Telerik.Reporting.Expressions.ExternalAssemblyFunctionLoader
.HasVisibleFunctionAttributes(Type type)
at Telerik.Reporting.Expressions.ExternalAssemblyFunctionLoader
.HasVisibleFunctionAttributes(Assembly assembly)
at Telerik.Reporting.Expressions.ExternalAssemblyFunctionLoader
.LoadFrom(Assembly assembly)
/* excluded for brevity */
at Telerik.Reporting.Processing.ReportProcessor.ProcessReportSource(...)
--- End of inner exception stack trace ---
at Telerik.Reporting.Processing.ReportProcessor.RenderReport(...)
at Stuf.Common.Reports.Specifics.ReportProcessor.RenderPDF(...)
at Stuf.Common.Reports.SmufReportGenerator.CreatePdf(...)
at Stuf.Common.Reports.SmufReportGenerator.GenerateReport(...)
Fără ajutor suplimentar e foarte greu de dibuit. A fost nevoie să urmăresc folosind ILSpy toate punctele înșirate în stack trace, după ce, în prealabil, mi-am pus cașcheta de voodoo. Ca-n Air Crash Investigation, catastrofa începe în cu totul alt loc, iar aici e vorba de următoarea funcție internă Telerik Reporting ce nu se află în calea urmată de excepție:
public static Type[] GetAssemblyTypes(Assembly assembly)
{
Type[] assemblyTypes = (Type[]) null;
try
{
assemblyTypes = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
assemblyTypes = ex.Types;
string str = string.Join<Exception>(
System.Environment.NewLine,
(IEnumerable<Exception>) ex.LoaderExceptions
);
FunctionLoader.TraceError(
$"ReflectionTypeLoadException occurred while " +
$"loading assembly {assembly.FullName}. " +
"Still, the successfully discovered types " +
"are loaded from the exception itself. " +
$"The loader exceptions are: {str}",
(Exception) ex
);
}
catch (Exception ex)
{
FunctionLoader.TraceError(
$"Failed to load functions from assembly " +
$"{assembly.FullName}",
ex
);
}
return assemblyTypes;
}
Aici avem câteva indicii valoroase:
– Orice excepție apare aici nu iese mai departe, fiind jurnalizată intern;
– În mod deosebit, la gestionarea ReflectionTypeLoadException se rețin tipurile ce-au putut fi încărcate (din ReflectionTypeLoadException.Types).
Dar există și un mic asterix: ReflectionTypeLoadException.Types poate conține elemente cu valoarea null pentru tipurile cu probleme de atitudine Prin proprietatea LoaderExceptions se pot stabili corespondențe între cine și ce-a făcut, cu cine-a studiat și la ce rezultate a ajuns. Acest comportament este documentat oficial:
An array of type Type containing the classes that were defined in the module and loaded. This array can contain some null values.
The LoaderExceptions property retrieves an array of type Exception that is parallel to this Types array. This array will contain null values whenever reflection cannot load a class.
Mai departe, Telerik Reporting străbate lista de tipuri pentru a face chestii cu ele, nu importă anume ce. Ce contează e că, tot din dâra de mai sus – în HasVisibleFunctionAttributes(), deși elementele null sunt o posibilitate acceptată din ipoteză, n-au făcut și verificările necesare evitării neplăcerilor, deci apelul evidențiat va conduce în cele din urmă la excepția raportată:
private bool HasVisibleFunctionAttributes(Assembly assembly)
{
if ((Assembly) null != assembly)
{
Type[] assemblyTypes = FunctionLoader.GetAssemblyTypes(assembly);
if (assemblyTypes != null)
{
foreach (Type type in assemblyTypes)
{
if (this.HasVisibleFunctionAttributes(type))
return true;
}
}
}
return false;
}
Dar toate balivernele astea nu ne spun decât că-i posibil și probabil ca o problemă la încărcarea unui DLL să conducă mai departe la o excepție System.ArgumentNullException. Prin urmare, nu-i cauza, ci doar una secundară după ce Telerik Report Engine se căznește să continue în siajul unei ReflectionTypeLoadException. Nu-i puțin lucru, aveam un indiciu unde mai înainte nu exista nimic.
Informații extinse sunt furnizate de librărie prin FunctionLoader.TraceError, o simplă fațadă peste jurnalizarea din System.Diagnostics. Pentru a obține mesajele, a fost nevoie să configurez un trace listener în app.config, sub elementul <configuration>:
<system.diagnostics>
<trace autoflush="true">
<listeners>
<remove name="Default" />
<add name="telerikFile"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="C:\...\Telerik.Reporting.trace.log"
/>
</listeners>
</trace>
</system.diagnostics>
Repetând testul, mi s-a confirmat presupunerea. Nu-i zice Iadul DLL-urilor degeaba, deși eu cred că o imagine mai potrivită e cea a lui Prometeu răstignit cu ficații făcuți ferfeniță. Chestiune de gust și de preferințe. Revenind:
StufZbuf.exe Error: 0 : ReflectionTypeLoadException occurred while loading assembly
Smuf.Zbuf, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null.
Still, the successfully discovered types are loaded from the exception itself.
The loader exceptions are:
System.IO.FileLoadException: Could not load file or assembly 'System.Web.Http, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
The located assembly's manifest definition does not match the assembly reference.
(Exception from HRESULT: 0x80131040)
File name: 'System.Web.Http, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
---> System.IO.FileLoadException: Could not load file or assembly 'System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
The located assembly's manifest definition does not match the assembly reference.
(Exception from HRESULT: 0x80131040)
File name: 'System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
Cu pași deciși de apropiem de victoria finală. Problema e că, măcar privitor la propriile dependințe, totul se prezenta, ca să citez organele, conform prevederilor legale. Adică referințele la System.Web.Http erau toate pentru versiunea 5.3.0.0, DLL-ul distribuit alăturea era tot 5.3.0.0, deci de unde cele două versiuni – 4.0.0.0, respectiv 5.2.7.0?
Din păcate am uitat să activez ceea este amintit și-n mesaje: assembly binding logging. Adică un fel de Stasi al mișcărilor de DLL-uri necesare rulării unei aplicații. De s-ar lăsa serviciile secrete duse la fel de ușor și-n viața reală în loc să ne pătrundă și-n chiloți spre binele nostru! Mă rog, iată ce-am obținut:
=== Pre-bind state information ===
LOG: DisplayName = System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
(Fully-specified)
LOG: Appbase = file:///D:/...
LOG: Initial PrivatePath = NULL
Calling assembly : Telerik.Reporting.Services.WebApi, Version=19.1.25.521, Culture=neutral, PublicKeyToken=a9d7983dfcc261be.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: D:\...\StufZbuf.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\...\machine.config.
LOG: Redirect found in application configuration file: 4.0.0.0 redirected to 5.2.7.0.
LOG: Post-policy reference: System.Web.Http, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
LOG: Attempting download of new URL file:///D:/.../System.Web.Http.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Minor Version
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
L-am prins în zbilț! Binarul Telerik.Reporting.Services.WebApi chiar cere System.Web.Http versiunea 4.0.0.0. Iar în fișierul de configurare StufZbuf.exe.Config chiar există o redirecționare către versiunea 5.2.7.0. Dar cea disponibilă este 5.3.0.0, deci nu există altă concluzie de bun simț decât că de vină-s rușii. Soluția? Taxe și impozite mărite.
Am recomandat actualizarea acelui <bindingRedirect /> la 5.3.0.0 și, pe viitor, de preferat cât mai curând, actualizarea Telerik Report Engine astfel încât măcar să nu fie o diferență atât de mare între versiunile dependințelor.