Fix null reference crash when opening AI chat with incomplete catalog data#298
Fix null reference crash when opening AI chat with incomplete catalog data#298
Conversation
…Case calls Co-authored-by: 239573049 <61819790+239573049@users.noreply.github.com>
There was a problem hiding this comment.
Pull Request Overview
This PR fixes a crash that occurs when clicking the AI chat button due to null/undefined document catalog item names. The fix adds defensive null checks in both frontend and backend code.
Key Changes:
- Added fallback handling for null/undefined document names in the frontend
- Added null safety checks for warehouse address in the backend
- Prevents
TypeError: Cannot read properties of null (reading 'toLowerCase')crash
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| web-site/src/components/layout/FumadocsSidebar/index.tsx | Added 'Untitled' fallback for null document names and null guard in filter function |
| src/KoalaWiki/Services/DocumentCatalogService.cs | Added null/empty check for warehouse address before string operations |
Comments suppressed due to low confidence (9)
src/KoalaWiki/Services/DocumentCatalogService.cs:313
- The expression 'A == false' can be simplified to '!A'.
disabled = item.IsCompleted == false
src/KoalaWiki/Services/DocumentCatalogService.cs:327
- The expression 'A == false' can be simplified to '!A'.
disabled = item.IsCompleted == false
src/KoalaWiki/Services/DocumentCatalogService.cs:451
- The expression 'A == false' can be simplified to '!A'.
disabled = child.IsCompleted == false
src/KoalaWiki/Services/DocumentCatalogService.cs:465
- The expression 'A == false' can be simplified to '!A'.
disabled = child.IsCompleted == false
src/KoalaWiki/Services/DocumentCatalogService.cs:48
- The expression 'A == false' can be simplified to '!A'.
.Where(x => x.WarehouseId.ToLower() == warehouse.Id.ToLower() && x.IsDeleted == false)
src/KoalaWiki/Services/DocumentCatalogService.cs:118
- The expression 'A == false' can be simplified to '!A'.
.Where(x => x.WarehouseId == warehouse.Id && x.Url == decodedPath && x.IsDeleted == false)
src/KoalaWiki/Services/DocumentCatalogService.cs:232
- Generic catch clause.
catch (Exception ex)
{
// 记录异常
Console.WriteLine($"更新目录失败: {ex.Message}");
return false;
}
src/KoalaWiki/Services/DocumentCatalogService.cs:260
- Generic catch clause.
catch (Exception ex)
{
// 记录异常
Console.WriteLine($"更新文档内容失败: {ex.Message}");
return false;
}
src/KoalaWiki/Services/DocumentCatalogService.cs:68
- Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.
if (documentCatalogs.Count == 0)
{
progress = 0;
}
else
{
progress = documentCatalogs.Count(x => x.IsCompleted) * 100 / documentCatalogs.Count;
}
| var url = string.Empty; | ||
|
|
||
| if (warehouse.Address.StartsWith("https://github.com") || warehouse.Address.StartsWith("https://gitee.com")) | ||
| if (warehouse != null && !string.IsNullOrEmpty(warehouse.Address)) |
There was a problem hiding this comment.
This null check is redundant. The Warehouse.Address property is defined as public string Address { get; set; } in KoalaWiki.Domains/Warehouse/Warehouse.cs (line 27), which means it's a non-nullable string type in C# and will never be null - it can only be an empty string.
The check !string.IsNullOrEmpty(warehouse.Address) is sufficient to handle the empty string case. The warehouse != null check can be removed since if warehouse were null, the code wouldn't reach this method call (it would have thrown an exception earlier in the calling method at line 112).
Suggested change:
if (!string.IsNullOrEmpty(warehouse.Address))
{
// ... existing code
}| if (warehouse != null && !string.IsNullOrEmpty(warehouse.Address)) | |
| if (!string.IsNullOrEmpty(warehouse.Address)) |
| menuItems | ||
| .filter(item => !searchQuery || | ||
| item.label.toLowerCase().includes(searchQuery.toLowerCase())) | ||
| (item.label && item.label.toLowerCase().includes(searchQuery.toLowerCase()))) |
There was a problem hiding this comment.
[nitpick] This null check for item.label is technically redundant because:
- The
MenuIteminterface defineslabelas a non-nullablestring(line 38) - The fallback is already applied when creating the
MenuItemon line 93:label: node.name || 'Untitled'
Since menuItems are created via the convertToMenuItem function that guarantees label is always a string, this check will never encounter a null/undefined label.
However, keeping this defensive check doesn't cause issues and provides extra safety in case the MenuItem creation logic changes in the future. If you prefer to keep it for defensive programming, that's fine, but you could also simplify it to just:
.filter(item => !searchQuery || item.label.toLowerCase().includes(searchQuery.toLowerCase()))| (item.label && item.label.toLowerCase().includes(searchQuery.toLowerCase()))) | |
| item.label.toLowerCase().includes(searchQuery.toLowerCase())) |
| return { | ||
| id: node.id, | ||
| label: node.name, | ||
| label: node.name || 'Untitled', // Fallback to 'Untitled' if name is null/undefined |
There was a problem hiding this comment.
The hardcoded string 'Untitled' should be internationalized using the translation function. Consider using t('repository.layout.untitled') or a similar i18n key to support multiple languages.
Suggested change:
label: node.name || t('repository.layout.untitled'),Don't forget to add the translation keys to all language files in web-site/src/i18n/locales/.
| label: node.name || 'Untitled', // Fallback to 'Untitled' if name is null/undefined | |
| label: node.name || t('repository.layout.untitled'), // Fallback to i18n 'Untitled' if name is null/undefined |
Clicking the AI chat button crashes with
TypeError: Cannot read properties of null (reading 'toLowerCase')when document catalog items have null/undefined names.Changes
Frontend (
web-site/src/components/layout/FumadocsSidebar/index.tsx)'Untitled'when converting DocumentNode with null name.toLowerCase()call in filter with null checkBackend (
src/KoalaWiki/Services/DocumentCatalogService.cs)warehouse.AddressinToFileSourcemethodPrevents crashes when repositories have incomplete metadata while maintaining all existing functionality.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
nuget.cdn.azure.cndotnet build KoalaWiki.sln(dns block)dotnet restore KoalaWiki.sln --disable-parallel(dns block)/opt/hostedtoolcache/CodeQL/2.23.3/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>点击AI聊天按钮报错</issue_title>
<issue_description>
Unexpected Application Error!
Cannot read properties of null (reading 'toLowerCase')
TypeError: Cannot read properties of null (reading 'toLowerCase')
at o (http://192.168.101.238:9002/static/index-Cc9hJDNy.js:2:11022)
at dt (http://192.168.101.238:9002/static/index-Cc9hJDNy.js:2:11593)
at Xs (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:48092)
at hc (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:70891)
at lp (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:81223)
at Np (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:116992)
at lv (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:116038)
at Fc (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:115870)
at Mp (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:112664)
at Zp (http://192.168.101.238:9002/static/index-KUBV1dvM.js:9:124583)
💿 Hey developer 👋
You can provide a way better UX than this when your app throws errors by providing your own ErrorBoundary or errorElement prop on your route.
前端页面
后台
[09:55:50 INF] Request starting HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr&branch=main - application/json null
[09:55:50 INF] Executing endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] Request starting HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr - application/json null
[09:55:50 INF] Executing endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] Executed endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 ERR] An error occurred while processing the request.
LibGit2Sharp.NotFoundException: 仓库不存在,请检查仓库名称和组织名称:idatachina iocr
at KoalaWiki.Services.DocumentCatalogService.GetDocumentCatalogsAsync(String organizationName, String name, String branch, String languageCode) in /src/src/KoalaWiki/Services/DocumentCatalogService.cs:line 39
at Microsoft.Extensions.DependencyInjection.FastExtensions.<>c.<b__11_1>d.MoveNext() in /src/src/KoalaWiki/obj/Release/net9.0/FastService.Analyzers/FastService.Analyzers.FastGenerator/FastExtensions.DocumentCatalog.g.cs:line 42
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.RequestDelegateFactory.g__ExecuteAwaited|129_0(Task
1 task, HttpContext httpContext, JsonTypeInfo1 jsonTypeInfo)at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger)
at KoalaWiki.Infrastructure.PermissionMiddleware.InvokeAsync(HttpContext context, IKoalaWikiContext dbContext) in /src/src/KoalaWiki/Infrastructure/PermissionMiddleware.cs:line 38
at KoalaWiki.Extensions.GlobalMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in /src/src/KoalaWiki/Extensions/GlobalMiddleware.cs:line 17
[09:55:50 ERR] HTTP GET /api/DocumentCatalog/DocumentCatalogs responded 500 in 9.5474 ms
[09:55:50 INF] Request finished HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr&branch=main - 500 null application/json; charset=utf-8 9.9051ms
[09:55:50 INF] Executed endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] HTTP GET /api/DocumentCatalog/DocumentCatalogs responded 200 in 23.4311 ms
[09:55:50 INF] Request finished HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr - 200 null application/json; charset=utf-8 23.6857ms
[09:55:50 INF] Request starting HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr&branch=master - application/json null
[09:55:50 INF] Executing endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] Executed endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] HTTP GET /api/DocumentCatalog/DocumentCatalogs responded 200 in 22.3144 ms
[09:55:50 INF] Request finished HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr&branch=master - 200 null application/json; charset=utf-8 22.4949ms
[09:55:50 INF] Request starting HTTP/1.1 GET http://192.168.101.238:9002/api/DocumentCatalog/DocumentCatalogs?organizationName=idatachina&name=iocr&branch=master - application/json null
[09:55:50 INF] Executing endpoint 'HTTP: GET /api/DocumentCatalog/documentcatalogs'
[09:55:50 INF] Request starting HTTP/1.1 G...
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.