{"record":{"id":"ea93fa8a9d77759a","repo":"gatsbyjs/gatsby","slug":"you-must-specify-either-a-cache-or-a-directory","errorCode":null,"errorMessage":"You must specify either a cache or a directory","messagePattern":"You must specify either a cache or a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/gatsby-core-utils/src/fetch-remote-file.ts","lineNumber":151,"sourceCode":"\nasync function fetchFile({\n  url,\n  cache,\n  directory,\n  auth = {},\n  httpHeaders = {},\n  ext,\n  name,\n  cacheKey,\n  excludeDigest,\n}: IFetchRemoteFileOptions): Promise<string> {\n  // global introduced in gatsby 4.0.0\n  const BUILD_ID = global.__GATSBY?.buildId ?? ``\n  const fileDirectory = (cache ? cache.directory : directory) as string\n  const storage = getStorage(getDatabaseDir())\n\n  if (!cache && !directory) {\n    throw new Error(`You must specify either a cache or a directory`)\n  }\n\n  const fetchFileMutex = createMutex(`gatsby-core-utils:fetch:${url}`)\n  await fetchFileMutex.acquire()\n\n  // Fetch the file.\n  try {\n    const digest = createContentDigest(url)\n    const finalDirectory = excludeDigest\n      ? path.resolve(fileDirectory)\n      : path.join(fileDirectory, digest)\n\n    if (!name) {\n      name = getRemoteFileName(url)\n    }\n\n    if (!ext) {\n      ext = getRemoteFileExtension(url)","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/packages/gatsby-core-utils/src/fetch-remote-file.ts#L133-L169","documentation":"Thrown by fetchRemoteFile when the caller supplies neither a `cache` object (with a .directory) nor a `directory` string. The function needs a writable location to store the downloaded file, and this guard refuses to proceed before computing paths or touching the mutex/db.","triggerScenarios":"Calling fetchRemoteFile({ url, ... }) with both `cache` and `directory` omitted; passing cache as undefined and directory as undefined/null; integrating the helper directly (outside gatsby-source-*) and forgetting the storage option.","commonSituations":"Custom source plugins or scripts invoking fetchRemoteFile standalone without forwarding the Gatsby cache; refactoring a source plugin and dropping the cache arg; migrating from an older signature that defaulted directory.","solutions":["Pass the Gatsby cache: fetchRemoteFile({ url, cache, ... }) — cache comes from the onCreateNode/etc. helper args and exposes .directory.","Or pass an explicit directory string if you manage storage yourself.","If you only need the bytes transiently, supply a temp directory under os.tmpdir()."],"exampleFix":"// before\nawait fetchRemoteFile({ url, ext, name })\n// after (forward cache from node helpers)\nawait fetchRemoteFile({ url, ext, name, cache })","handlingStrategy":"validation","validationCode":"if (!cache && !directory) throw new Error('cache or directory required')\nawait fetchRemoteFile({ url, cache, directory })","typeGuard":"const hasStorage = (o: { cache?: { directory: string }; directory?: string }): boolean =>\n  !!o?.cache?.directory || typeof o?.directory === 'string'","tryCatchPattern":null,"preventionTips":["Always forward the Gatsby `cache` from helper args when calling fetchRemoteFile.","Pass an explicit directory when managing storage yourself.","Wrap the call in a helper that asserts storage presence."],"tags":["fetch-remote-file","gatsby-core-utils","validation","cache"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}