{"record":{"id":"a5e1bb40498a5afc","repo":"SillyTavern/SillyTavern","slug":"theme-could-not-be-saved","errorCode":null,"errorMessage":"Theme could not be saved","messagePattern":"Theme could not be saved","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"public/scripts/power-user.js","lineNumber":2508,"sourceCode":"        }\n\n        name = await getSanitizedFilename(String(newName));\n    }\n\n    if (typeof theme !== 'object') {\n        theme = getThemeObject(name);\n    }\n\n    const response = await fetch('/api/themes/save', {\n        method: 'POST',\n        headers: getRequestHeaders(),\n        body: JSON.stringify(theme),\n    });\n\n    if (!response.ok) {\n        toastr.error('Check the server connection and reload the page to prevent data loss.', 'Theme could not be saved');\n        console.error('Theme could not be saved', response);\n        throw new Error('Theme could not be saved');\n    }\n\n    const themeIndex = themes.findIndex(x => x.name == name);\n\n    if (themeIndex == -1) {\n        themes.push(theme);\n        const option = document.createElement('option');\n        option.selected = true;\n        option.value = name;\n        option.innerText = name;\n        $('#themes').append(option);\n    } else {\n        themes[themeIndex] = theme;\n        $(`#themes option[value=\"${name}\"]`).prop('selected', true);\n    }\n\n    power_user.theme = name;\n    saveSettingsDebounced();","sourceCodeStart":2490,"sourceCodeEnd":2526,"githubUrl":"https://github.com/SillyTavern/SillyTavern/blob/8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8/public/scripts/power-user.js#L2490-L2526","documentation":"Thrown after POST /api/themes/save returns a non-ok HTTP response. The handler already fires a toastr error and console.error with the response object, then re-throws so callers know the save failed. It signals a server-side or transport failure, not a validation problem with the theme itself.","triggerScenarios":"saveTheme(name, theme) is invoked (theme dropdown change, new theme creation, CSS edit) while the server is unreachable, returns 4xx/5xx, or the session/auth has expired.","commonSituations":"Backend process stopped, network drop, expired CSRF/session token, full disk on the server, reverse-proxy body-size limit exceeded by large CSS, or a server restart lost in-memory state.","solutions":["Reload the page to refresh session/auth tokens, then retry the save.","Confirm the server process is running and reachable (server logs, health endpoint).","Inspect response.status and the response body in the DevTools Network tab for the specific failure code.","If the CSS is large, check server/proxy max-body-size and timeout limits."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Best-effort connectivity check before saving.\nasync function serverReachable() {\n  try {\n    const r = await fetch('/api/ping', { method: 'HEAD' });\n    return r.ok;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"// Retry transient failures a few times, then surface a clear message.\nasync function saveThemeSafe(name, theme, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await saveTheme(name, theme);\n    } catch (e) {\n      if (e.message !== 'Theme could not be saved' || i === attempts - 1) throw e;\n      await new Promise(r => setTimeout(r, 500 * (i + 1)));\n    }\n  }\n}","preventionTips":["Prompt the user to reload the page after long idle periods to refresh auth tokens.","Show an unsaved-changes indicator so a failed save is not silently lost.","Watch the Network tab for 401/403/500 on /api/themes/save to distinguish auth vs server faults."],"tags":["network","server","theme","persistence"],"backgroundTag":null,"analyzedSha":"8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8","analyzedAt":"2026-08-13T07:48:40.832Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}