diff --git a/web_src/js/features/repo-findfile.js b/web_src/js/features/repo-findfile.js
index 7b8833e793..f279dfeeb7 100644
--- a/web_src/js/features/repo-findfile.js
+++ b/web_src/js/features/repo-findfile.js
@@ -72,6 +72,10 @@ export function filterRepoFilesWeighted(files, filter) {
   return filterResult;
 }
 
+export function escapePath(s) {
+  return s.split('/').map(encodeURIComponent).join('/');
+}
+
 function filterRepoFiles(filter) {
   const treeLink = $repoFindFileInput.attr('data-url-tree-link');
   $repoFindFileTableBody.empty();
@@ -83,7 +87,7 @@ function filterRepoFiles(filter) {
   for (const r of filterResult) {
     const $row = $(tmplRow);
     const $a = $row.find('a');
-    $a.attr('href', `${treeLink}/${r.matchResult.join('')}`);
+    $a.attr('href', `${treeLink}/${escapePath(r.matchResult.join(''))}`);
     const $octiconFile = $(svg('octicon-file')).addClass('mr-3');
     $a.append($octiconFile);
     // if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz']
diff --git a/web_src/js/features/repo-findfile.test.js b/web_src/js/features/repo-findfile.test.js
index a90b0bf0a2..5032185396 100644
--- a/web_src/js/features/repo-findfile.test.js
+++ b/web_src/js/features/repo-findfile.test.js
@@ -1,5 +1,5 @@
 import {describe, expect, test} from 'vitest';
-import {strSubMatch, calcMatchedWeight, filterRepoFilesWeighted} from './repo-findfile.js';
+import {strSubMatch, calcMatchedWeight, filterRepoFilesWeighted, escapePath} from './repo-findfile.js';
 
 describe('Repo Find Files', () => {
   test('strSubMatch', () => {
@@ -32,4 +32,9 @@ describe('Repo Find Files', () => {
     expect(res).toHaveLength(2);
     expect(res[0].matchResult).toEqual(['', 'word', '.txt']);
   });
+
+  test('escapePath', () => {
+    expect(escapePath('a/b/c')).toEqual('a/b/c');
+    expect(escapePath('a/b/ c')).toEqual('a/b/%20c');
+  });
 });