alyssaxuu/screenity · error · Error

Invalid TUS location: ${err?.message || err}

Error message

Invalid TUS location: ${err?.message || err}

What it means

Thrown when the resolved TUS Location string cannot be parsed as a URL at all (new URL() threw) — the catch block rewraps any failure of the parsing/validation step, including the untrusted-host error from inside the try, into 'Invalid TUS location: ...'.

Source

Thrown at src/pages/CloudRecorder/bunnyTusUploader.js:1228

        )}`,
      },
    });

    if (!res.ok) throw new Error("Failed to start TUS upload session");
    const location = res.headers.get("location");
    const resolved = location.startsWith("/")
      ? `https://video.bunnycdn.com${location}`
      : location;
    // Defense-in-depth: TUS Location header must stay on Bunny's host. Without
    // this, a redirect to attacker.com would receive subsequent PATCHes
    // carrying recording chunks plus the AuthorizationSignature header.
    try {
      const parsed = new URL(resolved);
      if (parsed.host !== "video.bunnycdn.com") {
        throw new Error(`Untrusted TUS location host: ${parsed.host}`);
      }
    } catch (err) {
      throw new Error(`Invalid TUS location: ${err?.message || err}`);
    }
    this.uploadUrl = resolved;

    // Persist BEFORE save-upload-meta: the local journal is the only recovery
    // path if the extension crashes before the backend records the URL.
    await this.persistUploadJournal({ force: true });

    if (this.userToken) {
      fetch(`${API_BASE}/bunny/videos/save-upload-meta`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${this.userToken}`,
        },
        body: JSON.stringify({
          mediaId: this.mediaId,
          uploadUrl: this.uploadUrl,
          signature: this.signature,

View on GitHub (pinned to 512606387b)

Solutions

  1. Log the raw Location header value to see what was received
  2. Handle a null/missing Location header before constructing the URL and give a distinct error
  3. Verify no proxy or extension strips or mangles response headers
  4. Note that the untrusted-host error is rewrapped here — check the message suffix to distinguish the real cause

Example fix

// before
} catch (err) {
  throw new Error(`Invalid TUS location: ${err?.message || err}`);
}
// after
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Untrusted TUS location")) throw err;
  if (!location) throw new Error("TUS creation response missing Location header");
  throw new Error(`Invalid TUS location: ${err?.message || err}`);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!location) throw new Error("TUS create response missing Location header");
let parsed;
try { parsed = new URL(resolved); } catch { throw new Error("malformed TUS location"); }

Type guard

function isValidUrl(s) { try { new URL(s); return true; } catch { return false; } }

Try / catch

try {
  await uploader.init();
} catch (e) {
  if (String(e.message).startsWith("Invalid TUS location")) {
    console.warn("Location header issue:", e.message); // includes rewrapped causes
  }
}

Prevention

When it happens

Trigger: Location header missing, empty, or containing characters invalid for a URL so new URL() throws; also triggered by the inner untrusted-host throw being caught by this same try block and rewrapped.

Common situations: Proxy stripping the Location header so resolved is null/undefined; Bunny returning a relative path handled incorrectly; URL containing unencoded spaces or control characters.

Related errors


AI-assisted analysis of alyssaxuu/screenity@512606387b (2026-09-02). Data as JSON: /api/errors/1413c5c1384fea7c. Report an issue: GitHub.