anomalyco/sst · error

hash %s not found in manifest

Error message

hash %s not found in manifest

What it means

During uploadAssets, a hash sent by Cloudflare in an upload bucket could not be matched back to any path in the local AssetManifest (the manifest maps file path -> {hash,size,contentType}). This means the manifest handed to the upload session is out of sync with the files that session is asking for.

Source

Thrown at pkg/server/resource/cloudflare-worker-assets.go:230

}

func (r *WorkerAssets) uploadAssets(manifest AssetManifest, directory, accountId, apiToken string, hashes []string, jwt string) (string, error) {
	var body bytes.Buffer
	writer := multipart.NewWriter(&body)

	for _, hash := range hashes {
		// Find the file path for this hash in the manifest
		var fileKey string
		var contentType string
		for path, entry := range manifest {
			if entry.Hash == hash {
				fileKey = path
				contentType = entry.ContentType
				break
			}
		}
		if fileKey == "" {
			return "", fmt.Errorf("hash %s not found in manifest", hash)
		}
		
		// Read file content
		absFilePath := filepath.Join(directory, fileKey)
		fileContent, err := os.ReadFile(absFilePath)
		if err != nil {
			return "", fmt.Errorf("failed to read file %s: %w", absFilePath, err)
		}
		
		// Base64 encode the file content
		encodedContent := base64.StdEncoding.EncodeToString(fileContent)
		
		// Create form field with content type header
		part, err := writer.CreatePart(map[string][]string{
			"Content-Disposition": []string{fmt.Sprintf(`form-data; name="%s"; filename="%s"`, hash, hash)},
			"Content-Type":        []string{contentType},
		})
		if err != nil {

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Re-run the deploy without touching the assets directory so the manifest and files are consistent.
  2. Regenerate the asset manifest from the current build output and redeploy.
  3. Verify every entry.Hash in the manifest matches the actual file hash of the file at that path.
  4. Ensure the same manifest object is passed to both uploadAssetManifest and uploadAssets (no mutation in between).
  5. Check for concurrent builds writing to the same output directory during deploy.

Example fix

// before: manifest generated at build time, reused later after files changed
const manifest = JSON.parse(fs.readFileSync(".old-manifest.json"));
// after: rebuild manifest from current files immediately before upload
const manifest = buildManifestFromDirectory(assetDir);
Defensive patterns

Strategy: validation

Validate before calling

// before upload: every file hash in manifest must match the on-disk file,
// and the manifest must be freshly generated from the same build output
import { createHash } from "crypto";
function validateManifest(manifest, dir) {
  for (const [file, entry] of Object.entries(manifest)) {
    const p = path.join(dir, file);
    if (!fs.existsSync(p)) throw new Error(`manifest file missing: ${p}`);
    const h = createHash("md5").update(fs.readFileSync(p)).digest("hex");
    if (h !== entry.hash) throw new Error(`stale manifest: hash mismatch for ${file}`);
  }
}

Try / catch

try {
  await uploadAssets(manifest, dir);
} catch (e) {
  if (/hash .* not found in manifest/.test(String(e))) {
    throw new Error("Manifest out of sync with build output - regenerate the manifest and redeploy without modifying assets mid-deploy");
  }
  throw e;
}

Prevention

When it happens

Trigger: The manifest provided to handleUpload differs from the manifest used to initialize the session (stale build output), a file was rebuilt/changed between manifest generation and upload so hashes no longer match, or the manifest was trimmed/deduplicated incorrectly so a hash entry is missing.

Common situations: Editing files in the asset directory while a deploy is running; a build step regenerating assets after the manifest was computed; caching a manifest from a previous deploy and reusing it; content-addressing collisions or hash computation mismatch between local tooling and Cloudflare.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/9f992ebd581f5577. Report an issue: GitHub.