{"record":{"id":"8c3238eb61bf7c8d","repo":"gatsbyjs/gatsby","slug":"couldn-t-find-the-specified-offline-inject-script","errorCode":null,"errorMessage":"Couldn't find the specified offline inject script","messagePattern":"Couldn't find the specified offline inject script","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/gatsby-plugin-offline/src/gatsby-node.js","lineNumber":209,"sourceCode":"            `$1, debug: ${JSON.stringify(debug)}});`\n          )\n        fs.writeFileSync(swDest, swText)\n      }\n\n      const swAppend = fs\n        .readFileSync(`${__dirname}/sw-append.js`, `utf8`)\n        .replace(/%idbKeyValVersioned%/g, idbKeyValVersioned)\n        .replace(/%pathPrefix%/g, pathPrefix)\n        .replace(/%appFile%/g, appFile)\n\n      fs.appendFileSync(`public/sw.js`, `\\n` + swAppend)\n\n      if (appendScript !== null) {\n        let userAppend\n        try {\n          userAppend = fs.readFileSync(appendScript, `utf8`)\n        } catch (e) {\n          throw new Error(`Couldn't find the specified offline inject script`)\n        }\n        fs.appendFileSync(`public/sw.js`, `\\n` + userAppend)\n      }\n\n      reporter.info(\n        `Generated ${swDest}, which will precache ${count} files, totaling ${size} bytes.\\n` +\n          `The following pages will be precached:\\n` +\n          precachePages\n            .map(path => path.replace(`${process.cwd()}/public`, ``))\n            .join(`\\n`)\n      )\n    })\n}\n\nconst MATCH_ALL_KEYS = /^/\nexports.pluginOptionsSchema = function ({ Joi }) {\n  // These are the options of the v3: https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/#available-options\n  return Joi.object({","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/packages/gatsby-plugin-offline/src/gatsby-node.js#L191-L227","documentation":"gatsby-plugin-offline appends a user-supplied script to the generated service worker (public/sw.js) when the plugin option `appendScript` is set. The build reads that file from disk with fs.readFileSync; if the read fails (ENOENT, EACCES, etc.), the thrown system error is caught and re-thrown as this generic message. It fires during `createPages` / the Workbox generateSW step, so it aborts the whole Gatsby build.","triggerScenarios":"Setting the plugin option `appendScript` in gatsby-config.js to a path that does not resolve from the project root, e.g. { resolve: 'gatsby-plugin-offline', options: { appendScript: 'src/custom-sw.js' } } where the file is missing, mis-spelled, or outside the resolved cwd. It also triggers on unreadable files or paths that exist only in a different working directory than the build root.","commonSituations":"Renaming/deleting the injected SW script without updating gatsby-config.js; using a relative path that resolves differently in CI vs. local; typos in the option name or path; moving the file into a subfolder; case-sensitivity mismatches between macOS (case-insensitive) and Linux CI (case-sensitive).","solutions":["Verify the file exists at the exact path passed to `appendScript` (run `ls -la <path>` from the project root, the same cwd Gatsby uses).","Use a path relative to process.cwd() (project root), not relative to the plugin or to __dirname, e.g. options: { appendScript: './src/sw-inject.js' }.","Check spelling and case of every path segment, especially when developing on macOS/Windows and deploying to Linux.","If you no longer need a custom SW append, remove the `appendScript` option entirely (passing null is the default and skips the read).","Confirm the Gatsby process has read permission on the file (chmod / ownership) in CI and Docker builds."],"exampleFix":"// before (gatsby-config.js)\n{\n  resolve: `gatsby-plugin-offline`,\n  options: { appendScript: `src/cust-sw.js` } // file was moved\n}\n\n// after\n{\n  resolve: `gatsby-plugin-offline`,\n  options: { appendScript: `src/custom-sw-inject.js` } // matches actual file\n}","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst path = require('path');\nfunction assertAppendScriptExists(appendScript) {\n  if (appendScript == null) return; // null/undefined is the safe default\n  const resolved = path.isAbsolute(appendScript)\n    ? appendScript\n    : path.resolve(process.cwd(), appendScript);\n  if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {\n    throw new Error(`gatsby-plugin-offline appendScript not found: ${resolved}`);\n  }\n}\n// call before exporting gatsby-config plugins\nassertAppendScriptExists(process.env.GATSBY_OFFLINE_APPEND_SCRIPT);","typeGuard":"function isAppendScriptPath(v) {\n  return v == null || (typeof v === 'string' && v.trim().length > 0);\n}","tryCatchPattern":"let userAppend;\ntry {\n  userAppend = fs.readFileSync(appendScript, 'utf8');\n} catch (e) {\n  if (e.code === 'ENOENT') {\n    reporter.panic(`appendScript not found at ${appendScript} (cwd: ${process.cwd()})`);\n  }\n  throw e;\n}","preventionTips":["Treat appendScript as build-critical config: lint gatsby-config.js to assert the file exists.","Run gatsby build in CI with the same cwd you use locally.","Add a prebuild check (npm script) that verifies referenced asset paths exist."],"tags":["gatsby-plugin-offline","service-worker","filesystem","config","build"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}