amruthpillai/reactive-resume · error · Error

Unauthorized

Error message

Unauthorized

What it means

downloadResumePdf MCP tool resolves the caller from request headers via resolveUserFromRequestHeaders; if that returns null (no/invalid session) it throws a generic Error 'Unauthorized'. The tool does not rely on the transport's auth layer for this check — it explicitly verifies the user before signing the PDF download URL.

Source

Thrown at packages/mcp/src/tools.ts:167

		T.getResumeAnalysis,
		TOOL_META[T.getResumeAnalysis],
		withErrorHandling("getting resume analysis", async ({ id }: { id: string }) => {
			const analysis = await client.resume.analysis.getById({ id });

			if (!analysis) return text("No saved analysis for this resume yet.");

			return text(JSON.stringify(analysis, null, 2));
		}),
	);

	// ── Download Resume PDF ────────��──────────────────────────────
	server.registerTool(
		T.downloadResumePdf,
		TOOL_META[T.downloadResumePdf],
		withErrorHandling("creating PDF download URL", async ({ id }: { id: string }) => {
			const resume = await client.resume.getById({ id });
			const user = await resolveUserFromRequestHeaders(requestHeaders);
			if (!user) throw new Error("Unauthorized");

			const signedUrl = createResumePdfDownloadUrl({ resumeId: id, userId: user.id });

			return text(
				JSON.stringify(
					{
						resumeId: id,
						name: resume.name,
						downloadUrl: signedUrl.url,
						expiresAt: signedUrl.expiresAt,
						expiresInSeconds: signedUrl.expiresInSeconds,
						contentType: "application/pdf",
					},
					null,
					2,
				),
			);
		}),

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Authenticate the MCP session (sign in via the web app and reuse the session, or pass a valid bearer token) before invoking downloadResumePdf.
  2. Ensure the MCP client forwards Cookie/Authorization headers to the server transport.
  3. On 401/Unauthorized, prompt re-authentication rather than retrying blindly.
  4. Verify the authBaseUrl and cookie domain match between the MCP endpoint and the auth session.

Example fix

// before: no auth header
await mcp.tools.call('downloadResumePdf', { id });
// after: forward session
await mcp.tools.call('downloadResumePdf', { id }, { headers: { cookie: sessionCookie } });
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the MCP client is authenticated before calling downloadResumePdf
if (!await hasValidSession()) await promptReauth();

Type guard

function isUnauthorized(e: unknown): boolean {
  return e instanceof Error && /unauthorized/i.test(e.message);
}

Try / catch

try { await mcp.tools.call('downloadResumePdf', { id }); }
catch (e) {
  if (isUnauthorized(e)) { await promptReauth(); return; }
  throw e;
}

Prevention

When it happens

Trigger: Calling the MCP tool without a valid session cookie or Authorization header; an expired session; a transport that strips headers; the MCP server mounted without proxied auth headers.

Common situations: An MCP client (Claude/CLI/IDE) that didn't authenticate; a custom integration that forgot to forward credentials; session expired between listing resumes and requesting the download URL.

Understand the failure class

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/1bda1236129aaefa. Report an issue: GitHub.