Skip to content

Fix null reference crash when opening AI chat with incomplete catalog data#298

Merged
239573049 merged 2 commits intomainfrom
copilot/fix-ai-chat-button-error
Nov 17, 2025
Merged

Fix null reference crash when opening AI chat with incomplete catalog data#298
239573049 merged 2 commits intomainfrom
copilot/fix-ai-chat-button-error

Conversation

Copy link
Contributor

Copilot AI commented Nov 17, 2025

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)

  • Add fallback to 'Untitled' when converting DocumentNode with null name
  • Guard .toLowerCase() call in filter with null check
// Before
label: node.name,
.filter(item => !searchQuery || item.label.toLowerCase().includes(...))

// After  
label: node.name || 'Untitled',
.filter(item => !searchQuery || (item.label && item.label.toLowerCase().includes(...)))

Backend (src/KoalaWiki/Services/DocumentCatalogService.cs)

  • Add null check before accessing warehouse.Address in ToFileSource method
// Before
if (warehouse.Address.StartsWith("https://github.com"))

// After
if (warehouse != null && !string.IsNullOrEmpty(warehouse.Address))
{
    if (warehouse.Address.StartsWith("https://github.com"))
}

Prevents 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.cn
    • Triggering command: dotnet build KoalaWiki.sln (dns block)
    • Triggering command: dotnet restore KoalaWiki.sln --disable-parallel (dns block)
    • Triggering command: /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>Image

Image

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(Task1 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.

…Case calls

Co-authored-by: 239573049 <61819790+239573049@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix unexpected application error for AI chat button Fix null reference crash when opening AI chat with incomplete catalog data Nov 17, 2025
Copilot AI requested a review from 239573049 November 17, 2025 10:24
@239573049 239573049 marked this pull request as ready for review November 17, 2025 11:36
Copilot AI review requested due to automatic review settings November 17, 2025 11:36
@239573049 239573049 merged commit 5e198d8 into main Nov 17, 2025
@239573049 239573049 deleted the copilot/fix-ai-chat-button-error branch November 17, 2025 11:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
}
Suggested change
if (warehouse != null && !string.IsNullOrEmpty(warehouse.Address))
if (!string.IsNullOrEmpty(warehouse.Address))

Copilot uses AI. Check for mistakes.
menuItems
.filter(item => !searchQuery ||
item.label.toLowerCase().includes(searchQuery.toLowerCase()))
(item.label && item.label.toLowerCase().includes(searchQuery.toLowerCase())))
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This null check for item.label is technically redundant because:

  1. The MenuItem interface defines label as a non-nullable string (line 38)
  2. The fallback is already applied when creating the MenuItem on 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()))
Suggested change
(item.label && item.label.toLowerCase().includes(searchQuery.toLowerCase())))
item.label.toLowerCase().includes(searchQuery.toLowerCase()))

Copilot uses AI. Check for mistakes.
return {
id: node.id,
label: node.name,
label: node.name || 'Untitled', // Fallback to 'Untitled' if name is null/undefined
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/.

Suggested change
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

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

点击AI聊天按钮报错

3 participants