GoogleChrome/lighthouse · warning · Error
Save already in progress
Error message
Save already in progress
What it means
Thrown by GithubApi.createGist to guard against overlapping saves: a class-level _saving flag is set true at the start of a gist creation and only reset in a finally block. A second createGist call while the first is still in flight is rejected outright. This is a re-entrancy guard, not a network error.
Source
Thrown at viewer/app/src/github-api.js:41
this._saving = false;
}
static get LH_JSON_EXT() {
return '.lighthouse.report.json';
}
getFirebaseAuth() {
return this._auth;
}
/**
* Creates a gist under the users account.
* @param {LH.Result|LH.FlowResult} jsonFile The gist file body.
* @return {Promise<string>} id of the created gist.
*/
async createGist(jsonFile) {
if (this._saving) {
throw new Error('Save already in progress');
}
logger.log('Saving report to GitHub...', false);
this._saving = true;
try {
const accessToken = await this._auth.getAccessToken();
let filename;
if ('steps' in jsonFile) {
filename = getFlowResultFilenamePrefix(jsonFile);
} else {
filename = getLhrFilenamePrefix({
finalDisplayedUrl: Util.getFinalDisplayedUrl(jsonFile),
fetchTime: jsonFile.fetchTime,
});
}
const body = {
description: 'Lighthouse json report',View on GitHub (pinned to 9515cd4e58)
Solutions
- Disable the save UI control while createGist is pending and re-enable it on settlement (success or error).
- Await/queue the existing save instead of starting a new one — guard with a local promise or debounce.
- Only call createGist when not already saving (check the in-flight promise at the call site).
Example fix
// before
saveBtn.onclick = () => githubApi.createGist(json); // double-click throws
// after
let pending = null;
saveBtn.onclick = async () => {
if (pending) return pending; // reuse in-flight save
saveBtn.disabled = true;
pending = githubApi.createGist(json).finally(() => {
pending = null;
saveBtn.disabled = false;
});
return pending;
}; Defensive patterns
Strategy: validation
Validate before calling
let pendingSave = null;
async function safeCreateGist(json) {
if (pendingSave) return pendingSave;
pendingSave = githubApi.createGist(json).finally(() => { pendingSave = null; });
return pendingSave;
} Type guard
// The _saving flag is internal; mirror it at the call site with an in-flight promise. const isAlreadySaving = () => pendingSave !== null;
Try / catch
try {
await safeCreateGist(json);
} catch (err) {
if (err.message === 'Save already in progress') {
// ignore — a save is already running; optionally await it
} else throw err;
} Prevention
- Disable the save control during the in-flight createGist call.
- Track an in-flight promise and reuse it instead of starting a parallel save.
- Debounce button clicks on the save action.
When it happens
Trigger: Invoking createGist a second time before the first awaited call resolves — e.g. double-clicking the 'Save as gist' button, or two callers racing to save.
Common situations: Users double-clicking the save button; a UI that does not disable the control during the async save; programmatic retry triggered before the prior promise settles.
Related errors
- Error: ${JSON.stringify(json)}
- ${resp.status} fetching gist
- Failed to find a Lighthouse report (*${GithubApi.LH_JSON_EXT
- Invalid API response: ${await response.text()}
AI-assisted analysis of GoogleChrome/lighthouse@9515cd4e58 (2026-08-13).
Data as JSON: /api/errors/ad9be17f8fbc2f79.
Report an issue: GitHub.