{"record":{"id":"22bbd332cf8da6c7","repo":"anomalyco/sst","slug":"failed-to-read-file-s-w","errorCode":null,"errorMessage":"failed to read file %s: %w","messagePattern":"failed to read file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/server/resource/cloudflare-worker-assets.go","lineNumber":237,"sourceCode":"\t\t// Find the file path for this hash in the manifest\n\t\tvar fileKey string\n\t\tvar contentType string\n\t\tfor path, entry := range manifest {\n\t\t\tif entry.Hash == hash {\n\t\t\t\tfileKey = path\n\t\t\t\tcontentType = entry.ContentType\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif fileKey == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"hash %s not found in manifest\", hash)\n\t\t}\n\t\t\n\t\t// Read file content\n\t\tabsFilePath := filepath.Join(directory, fileKey)\n\t\tfileContent, err := os.ReadFile(absFilePath)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to read file %s: %w\", absFilePath, err)\n\t\t}\n\t\t\n\t\t// Base64 encode the file content\n\t\tencodedContent := base64.StdEncoding.EncodeToString(fileContent)\n\t\t\n\t\t// Create form field with content type header\n\t\tpart, err := writer.CreatePart(map[string][]string{\n\t\t\t\"Content-Disposition\": []string{fmt.Sprintf(`form-data; name=\"%s\"; filename=\"%s\"`, hash, hash)},\n\t\t\t\"Content-Type\":        []string{contentType},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to create form part %s: %w\", hash, err)\n\t\t}\n\n\t\t_, err = part.Write([]byte(encodedContent))\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to write encoded content for %s: %w\", hash, err)\n\t\t}","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/server/resource/cloudflare-worker-assets.go#L219-L255","documentation":"uploadAssets resolved a manifest entry to an absolute path (directory + fileKey) and os.ReadFile failed, so the file's contents could not be base64-encoded and uploaded. The wrapped OS error in the message says whether the file is missing (no such file or directory), unreadable (permission denied), or is a directory.","triggerScenarios":"The asset file at directory/fileKey does not exist at upload time (deleted or renamed after the manifest was built), directory points at the wrong local folder, file permissions block reading, or fileKey contains path separators that don't resolve on the current OS.","commonSituations":"Deploying from a machine/CI where the build output directory path differs from the configured 'directory' input; assets cleaned by a later build step before upload; case-sensitive vs case-insensitive filesystem mismatches (macOS dev vs Linux CI); permission-restricted files in the output dir.","solutions":["Check the wrapped OS error: 'no such file' means fix the directory path or rebuild assets; 'permission denied' means fix file permissions.","Verify the 'directory' input matches the actual build output folder (absolute path recommended).","Rebuild assets before deploy and ensure no cleanup step runs between manifest generation and upload.","Check file name casing matches exactly (Linux CI filesystems are case-sensitive).","Ensure the deploy runs on the same machine/container where the assets were built."],"exampleFix":"// before: relative directory resolved from wrong cwd\nDirectory: \"dist/client\"\n// after: absolute path anchored to the project root\nDirectory: path.resolve(__root, \"dist/client\")","handlingStrategy":"validation","validationCode":"// pre-flight: every manifest entry must exist and be readable at directory/fileKey\nimport fs from \"fs\";\nimport path from \"path\";\nfunction validateAssetFiles(manifest, dir) {\n  for (const file of Object.keys(manifest)) {\n    const p = path.resolve(dir, file);\n    const st = fs.statSync(p); // throws ENOENT/EACCES early with a clear path\n    if (!st.isFile()) throw new Error(`not a file: ${p}`);\n    fs.accessSync(p, fs.constants.R_OK);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await uploadAssets(manifest, dir);\n} catch (e) {\n  if (String(e).startsWith(\"failed to read file\")) {\n    const p = String(e).match(/failed to read file (.*?):/)?.[1];\n    throw new Error(`Asset file unreadable at ${p} - check 'directory' path, file casing, and permissions`);\n  }\n  throw e;\n}","preventionTips":["Configure 'directory' as an absolute path to the actual build output","Run the deploy on the same machine/container where assets were built","Match filename casing exactly (Linux CI is case-sensitive)","Ensure no cleanup step deletes assets between build and upload"],"tags":["filesystem","io","assets","path"],"backgroundTag":"file-not-found","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}