can1357/oh-my-pi · error · Error

seafile share response did not include a Location URL

Error message

seafile share response did not include a Location URL

What it means

The Seafile uploader expects the share-link creation response to yield a public URL, either from the Location header of a redirect (fetch is called with redirect: "manual") or from the JSON body's shareLink field. If neither is present, the response cannot be turned into a share URL and the upload fails fast with this error. It guards against silent misconfiguration of the Seafile server or an unexpected API response shape.

Solutions

  1. Verify the Seafile API base URL and token: curl the share-link endpoint manually and inspect the response body/headers
  2. Check the Seafile server version and confirm it returns shareLink (or sets Location) on POST /api/v2.1/share-links/
  3. Check for a reverse proxy rewriting/stripping the Location header
  4. Inspect the raw response body in logs to see the server's error message (e.g. permission denied on the library)

Example fix

// before: server returns error JSON with no shareLink
if (!location) throw new Error("seafile share response did not include a Location URL");
// after: log the body to diagnose
if (!location) throw new Error(`seafile share response did not include a Location URL: ${await shareResponse.text()}`);
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch(shareUrl, { method: "POST", headers, redirect: "manual" });
const loc = res.headers.get("Location") ?? (await res.json())?.shareLink;
if (!loc) throw new Error(`Seafile returned no share link: HTTP ${res.status}`);

Type guard

function hasShareLink(v: unknown): v is { shareLink: string } {
	return typeof v === "object" && v !== null && typeof (v as { shareLink?: unknown }).shareLink === "string";
}

Try / catch

try {
	await uploader.upload(req);
} catch (err) {
	if (err instanceof Error && err.message.includes("seafile share response")) {
		// log Seafile base URL/token, inspect server response, surface actionable message
	}
	throw err;
}

Prevention

When it happens

Trigger: The POST to the Seafile share-link endpoint (api/v2.1/share-links/) returns 2xx but includes no Location header and its JSON body lacks a usable shareLink value — e.g. the server responds with an empty body, an error object with no link field, or a non-redirect response because the endpoint path or auth token is wrong.

Common situations: Misconfigured SEAFILE server URL or API token causing the server to answer with a JSON error instead of a share link; a Seafile version whose share-link response format differs (older API versions return different field names); a reverse proxy stripping Location headers on manual-redirect responses.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8dd88b691fed1a2a. Report an issue: GitHub.

Appendix: source

Thrown at packages/coding-agent/src/blob-broker/uploaders-self-hosted.ts:433

			const shareForm = new URLSearchParams({
				p: `${directory === "/" ? "" : directory}/${filename}`,
				share_type: "download",
			});
			if (sharePassword) shareForm.set("password", sharePassword);
			if (expiryDays !== undefined && expiryDays > 0) shareForm.set("expire", String(expiryDays));
			const shareResponse = await expectOk(
				await requestFetch(`${endpoint(apiUrl, "repos", repositoryId, "file", "shared-link")}/`, {
					method: "PUT",
					headers: { ...headers, "Content-Type": "application/x-www-form-urlencoded" },
					body: shareForm,
					redirect: "manual",
				}),
				"seafile",
			);
			let location = shareResponse.headers.get("Location") ?? undefined;
			if (!location) location = shareLink(parseJsonText(await shareResponse.text()));
			if (!location) throw new Error("seafile share response did not include a Location URL");
			const resultUrl = new URL(location, apiUrl);
			if (raw) resultUrl.searchParams.set("raw", "1");
			return publication("seafile", request, resultUrl.toString(), {
				...(expiryInfo.expiresAt === undefined ? {} : { expiresAt: expiryInfo.expiresAt }),
			});
		},
	};
}

function plikFile(value: unknown): PlikFile | undefined {
	let candidate: unknown = value;
	if (Array.isArray(candidate)) candidate = candidate[0];
	if (typeof candidate === "object" && candidate !== null && "file" in candidate) candidate = candidate.file;
	if (typeof candidate !== "object" || candidate === null || !("id" in candidate)) return undefined;
	const id = identifier(candidate.id);
	if (!id) return undefined;
	const name =
		"fileName" in candidate

View on GitHub (pinned to 9690622007)