diff --git a/playwright.config.js b/playwright.config.js
index fdf6514f26..808e3ca261 100644
--- a/playwright.config.js
+++ b/playwright.config.js
@@ -46,10 +46,10 @@ export default {
     locale: 'en-US',
 
     /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
-    actionTimeout: 1000,
+    actionTimeout: 2000,
 
     /* Maximum time allowed for navigation, such as `page.goto()`. */
-    navigationTimeout: 5 * 1000,
+    navigationTimeout: 10 * 1000,
 
     /* Base URL to use in actions like `await page.goto('/')`. */
     baseURL: BASE_URL,
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index 1a711fcb91..db21adae2c 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -22,7 +22,7 @@
 	{{if $canEditIssueTitle}}
 	<div class="ui form issue-title tw-hidden" id="issue-title-editor">
 		<div class="ui input tw-flex-1">
-			<input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="255" autocomplete="off" class="js-quick-submit">
+			<input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="245" autocomplete="off" class="js-quick-submit">
 		</div>
 		<div class="button-row">
 			<button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
diff --git a/tests/e2e/issue-sidebar.test.e2e.js b/tests/e2e/issue-sidebar.test.e2e.js
index f06748c7a3..3bda7bdd17 100644
--- a/tests/e2e/issue-sidebar.test.e2e.js
+++ b/tests/e2e/issue-sidebar.test.e2e.js
@@ -51,6 +51,22 @@ test('Pull: Toggle WIP', async ({browser}, workerInfo) => {
   // remove again
   await click_toggle_wip({page});
   await check_wip({page}, false);
+  // check maximum title length is handled gracefully
+  const maxLenStr = prTitle + 'a'.repeat(240);
+  await page.locator('#issue-title-edit-show').click();
+  await page.locator('#issue-title-editor input').fill(maxLenStr);
+  await page.getByText('Save').click();
+  await page.waitForLoadState('networkidle');
+  await click_toggle_wip({page});
+  await check_wip({page}, true);
+  await click_toggle_wip({page});
+  await check_wip({page}, false);
+  await expect(page.locator('h1')).toContainText(maxLenStr);
+  // restore original title
+  await page.locator('#issue-title-edit-show').click();
+  await page.locator('#issue-title-editor input').fill(prTitle);
+  await page.getByText('Save').click();
+  await check_wip({page}, false);
 });
 
 test('Issue: Labels', async ({browser}, workerInfo) => {
diff --git a/tests/e2e/utils_e2e.js b/tests/e2e/utils_e2e.js
index f514fc9c49..98d762fbcc 100644
--- a/tests/e2e/utils_e2e.js
+++ b/tests/e2e/utils_e2e.js
@@ -22,6 +22,7 @@ const LOGIN_PASSWORD = 'password';
 // log in user and store session info. This should generally be
 //  run in test.beforeAll(), then the session can be loaded in tests.
 export async function login_user(browser, workerInfo, user) {
+  test.setTimeout(60000);
   // Set up a new context
   const context = await test_context(browser);
   const page = await context.newPage();