windmill-labs/windmill · error · GitHubAppError

UNKNOWN_ERROR

UNKNOWN_ERROR

Error message

No JWT token received from server

What it means

After GitSyncService.exportInstallation() returns, exportInstallation() checks that the server response contains a jwt_token field and throws this UNKNOWN_ERROR if it is missing/empty. It guards against a server or proxy responding 2xx with an unexpected body, before the token is copied to the clipboard.

Source

Thrown at frontend/src/lib/githubApp.ts:333

	currentWorkspace: string,
	installationId: number
): Promise<void> {
	// Input validation
	if (!currentWorkspace) {
		throw createGitHubAppError('Workspace is required', 'VALIDATION_ERROR')
	}
	if (!installationId || installationId <= 0) {
		throw createGitHubAppError('Valid installation ID is required', 'VALIDATION_ERROR')
	}

	try {
		const response = await GitSyncService.exportInstallation({
			workspace: currentWorkspace,
			installationId: installationId
		})

		if (!response.jwt_token) {
			throw createGitHubAppError('No JWT token received from server', 'UNKNOWN_ERROR')
		}

		const jwtToken = response.jwt_token

		// Copy to clipboard with fallback for unsecure contexts
		if (navigator.clipboard && navigator.clipboard.writeText) {
			await navigator.clipboard.writeText(jwtToken)
			sendUserToast(
				'JWT token copied to clipboard. This token is sensitive and should be kept secret!',
				false,
				undefined,
				undefined,
				10000
			)
		} else {
			// Fallback: show the token in the toast for manual copying
			sendUserToast(
				`JWT token (copy manually): ${jwtToken}`,

View on GitHub (pinned to e474e8803c)

Solutions

  1. Verify frontend and backend versions match and the git_sync export endpoint returns { jwt_token: ... }.
  2. Inspect the raw HTTP response (network tab) to see what the server actually returned.
  3. Check for a reverse proxy or gateway altering the response body/status.
  4. Log out / re-login to rule out a stale session producing a degraded response.

Example fix

// before
const response = await GitSyncService.exportInstallation({ workspace, installationId })
copyToClipboard(response.jwt_token)
// after
const response = await GitSyncService.exportInstallation({ workspace, installationId })
if (!response?.jwt_token) throw new Error('Server returned no jwt_token')
copyToClipboard(response.jwt_token)
Defensive patterns

Strategy: try-catch

Type guard

function hasJwtToken(r: unknown): r is { jwt_token: string } {
  return typeof r === 'object' && r !== null && typeof (r as any).jwt_token === 'string' && (r as any).jwt_token.length > 0
}

Try / catch

try {
  await exportInstallation(workspace, installationId)
} catch (e) {
  if (e?.message?.includes('No JWT token')) {
    toast.error('Server did not return a token — check frontend/backend versions and proxy')
  } else throw e
}

Prevention

When it happens

Trigger: The backend endpoint for git_sync app installation export returns 200 but with a body lacking jwt_token — e.g. an API version mismatch where the field was renamed, an HTML error page served by a reverse proxy with status 200, or the server serializing an empty object.

Common situations: Frontend and backend deployed at mismatched versions (field renamed/removed); a dev proxy intercepting the request; the instance behind an auth gateway that rewrites responses; backend bug returning success without generating the token.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/12a30b51f3bd1715. Report an issue: GitHub.