anomalyco/sst · error

failed to initialize assets upload: no JWT received

Error message

failed to initialize assets upload: no JWT received

What it means

Cloudflare returned HTTP 200 for the assets-upload-session request, but the decoded result.jwt field was empty. The JWT is the session token required to authorize subsequent asset uploads, so the library refuses to continue without it. This typically indicates an unexpected API response shape or an API-side anomaly.

Source

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

	}
	defer resp.Body.Close()

	// print out response body as a string
	if resp.StatusCode != http.StatusOK {
		responseBody, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("failed to initialize assets upload: HTTP %d %s", resp.StatusCode, string(responseBody))
	}

	var result struct {
		Result InitializeAssetsResponse `json:"result"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}

	if result.Result.Jwt == "" {
		return nil, fmt.Errorf("failed to initialize assets upload: no JWT received")
	}

	return &result.Result, nil
}

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

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Log the raw response body when jwt is empty to see what Cloudflare actually returned.
  2. Retry the deploy — transient API-side session failures often clear.
  3. Check Cloudflare's API changelog/status for changes to the assets-upload-session response schema.
  4. Ensure no proxy or corporate gateway is rewriting the response (decode errors or empty bodies from HTML pages).
  5. Report/upgrade if the platform's response parsing is out of date with the current Cloudflare API.
Defensive patterns

Strategy: retry

Validate before calling

// after a session call, assert the envelope before trusting it
if (!data.success || !data.result || typeof data.result.jwt !== "string" || !data.result.jwt) {
  throw new Error(`unexpected session response: ${JSON.stringify(data).slice(0, 500)}`);
}

Type guard

function hasJwt(r: unknown): r is { result: { jwt: string; buckets: string[][] } } {
  const o = r as any;
  return !!o && typeof o === "object" &&
    typeof o.result?.jwt === "string" && o.result.jwt.length > 0 &&
    Array.isArray(o.result?.buckets);
}

Try / catch

try {
  session = await initAssetSession(manifest);
} catch (e) {
  if (String(e).includes("no JWT received")) {
    console.error("Cloudflare returned 200 without a session JWT - check raw response/API version");
    session = await retryWithBackoff(() => initAssetSession(manifest), { attempts: 3 });
  } else throw e;
}

Prevention

When it happens

Trigger: Cloudflare responds 200 but with a JSON body whose result.jwt is missing or empty — e.g. an API schema change, an error payload wrapped in a 200, or a response where result.success is false but the HTTP status is still 200.

Common situations: Cloudflare API version drift changing the response envelope; the token being valid but the session creation silently failing server-side; proxy/gateway injecting a 200 HTML or partial JSON page that decodes into a zero-value struct.

Related errors


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