{"record":{"id":"49bc1b77c1642319","repo":"GoogleChrome/lighthouse","slug":"error-json-stringify-json","errorCode":null,"errorMessage":"Error: ${JSON.stringify(json)}","messagePattern":"Error: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"viewer/app/src/github-api.js","lineNumber":82,"sourceCode":"          },\n        },\n      };\n      const request = new Request('https://api.github.com/gists', {\n        method: 'POST',\n        headers: new Headers({Authorization: `token ${accessToken}`}),\n        // Stringify twice so quotes are escaped for POST request to succeed.\n        body: JSON.stringify(body),\n      });\n      const response = await fetch(request);\n      if (response.status === 401) {\n        this._auth.signOut();\n      }\n      const json = await response.json();\n      if (json.id) {\n        logger.log('Saved!');\n        return json.id;\n      } else {\n        throw new Error('Error: ' + JSON.stringify(json));\n      }\n    } finally {\n      this._saving = false;\n    }\n  }\n\n  /**\n   * Fetches a Lighthouse report from a gist.\n   * @param {string} id The id of a gist.\n   * @return {Promise<LH.Result>}\n   */\n  getGistFileContentAsJson(id) {\n    logger.log('Fetching report from GitHub...', false);\n\n    return this._auth.getAccessTokenIfLoggedIn().then(accessToken => {\n      const headers = new Headers();\n\n      // If there's an authenticated token, include an Authorization header to","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/viewer/app/src/github-api.js#L64-L100","documentation":"Thrown by GithubApi.createGist when the GitHub API POST /gists response parsed as JSON does not contain an 'id' field. A successful gist creation returns the gist id; any response lacking it is treated as a failure and the whole response body is stringified into the error message for diagnosis. A 401 response additionally triggers sign-out before this throw is reached.","triggerScenarios":"GitHub returns an error payload (rate-limit message, validation error, 422 unprocessable entity, permission error) instead of a gist object, so json.id is undefined.","commonSituations":"GitHub API rate limiting (secondary rate limits return JSON errors with HTTP 200/403); token lacking gist scope; gist content too large; transient GitHub API error responses.","solutions":["Inspect the stringified JSON in the error message — it typically states the problem (e.g. 'rate limit', 'Validation Failed').","If rate-limited, wait and retry; ensure the access token has the 'gist' scope.","Wrap createGist in try/catch and show the parsed GitHub message to the user."],"exampleFix":"// before\nconst id = await githubApi.createGist(json); // unhandled on API error\n\n// after\ntry {\n  const id = await githubApi.createGist(json);\n} catch (err) {\n  const apiMsg = err.message; // contains JSON.stringify(githubResponse)\n  showUserError(`Could not save gist: ${apiMsg}`);\n}","handlingStrategy":"try-catch","validationCode":"// No pre-call validation possible (server decides); validate the token scope instead.\nconst token = await auth.getAccessToken();\n// Tokens must have the 'gist' scope; verify out-of-band before saving.","typeGuard":null,"tryCatchPattern":"try {\n  const id = await githubApi.createGist(json);\n} catch (err) {\n  // err.message is 'Error: ' + JSON.stringify(githubResponse)\n  const apiError = (() => { try { return JSON.parse(err.message.replace(/^Error:\\s*/, '')); } catch { return null; } })();\n  showUserError(apiError?.message ? `GitHub: ${apiError.message}` : err.message);\n}","preventionTips":["Ensure the access token has the 'gist' scope before attempting to save.","Respect GitHub rate limits; back off if you see rate-limit messages in the response.","Keep payload size reasonable; very large reports can be rejected."],"tags":["github-api","gist","network","rate-limit"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}