different-ai/openwork · error · ProbeFailure

AUTH_USER_OR_WORKLOAD

AUTH_USER_OR_WORKLOAD

Error message

Authorization response had no redirect location

What it means

The probe sends the authorization request with redirect: "manual" and expects a 302 response carrying a `location` header (the OAuth callback containing the code and state). When the response is a 302 but has no location header — or when a non-302 response passed expectOk yet lacks one — the probe cannot continue the code flow and throws AUTH_USER_OR_WORKLOAD / oauth_authorization.

Source

Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:647

    const oauthState = randomBytes(16).toString("base64url")
    sensitiveValues.push(verifier, oauthState)
    const redirectUri = scenario.oauth.redirectUris[0]
    if (!redirectUri) throw new ProbeFailure("CONFIGURATION", "configuration", "Scenario has no redirect URI")
    const authorizeUrl = new URL(authorizationEndpoint)
    authorizeUrl.searchParams.set("response_type", "code")
    authorizeUrl.searchParams.set("client_id", clientId)
    authorizeUrl.searchParams.set("redirect_uri", redirectUri)
    authorizeUrl.searchParams.set("scope", scenario.oauth.authorizationScopes.join(" "))
    authorizeUrl.searchParams.set("resource", mcpUrl)
    authorizeUrl.searchParams.set("state", oauthState)
    authorizeUrl.searchParams.set("code_challenge", pkceChallengeValue)
    authorizeUrl.searchParams.set("code_challenge_method", "S256")
    startedAt = Date.now()
    const authorizeResponse = await fetchStep(authorizeUrl, { redirect: "manual" }, "AUTH_USER_OR_WORKLOAD", overallDeadline)
    if (authorizeResponse.status !== 302) await expectOk(authorizeResponse, "AUTH_USER_OR_WORKLOAD")
    else await discardResponseBody(authorizeResponse, "AUTH_USER_OR_WORKLOAD", "oauth_authorization")
    const location = authorizeResponse.headers.get("location")
    if (!location) throw new ProbeFailure("AUTH_USER_OR_WORKLOAD", "oauth_authorization", "Authorization response had no redirect location")
    const callback = new URL(location)
    const callbackWithoutResponse = new URL(callback)
    callbackWithoutResponse.searchParams.delete("code")
    callbackWithoutResponse.searchParams.delete("state")
    if (callbackWithoutResponse.href !== redirectUri) {
      throw new ProbeFailure("AUTH_USER_OR_WORKLOAD", "oauth_authorization", "Authorization callback did not exactly match the registered redirect URI")
    }
    if (callback.searchParams.get("state") !== oauthState || !callback.searchParams.get("code")) {
      throw new ProbeFailure("AUTH_USER_OR_WORKLOAD", "oauth_authorization", "Authorization callback state or code was invalid")
    }
    sensitiveValues.push(callback.searchParams.get("code") ?? "")
    recordPassed(phases, "AUTH_USER_OR_WORKLOAD", startedAt, "Synthetic user authorization and state binding passed")

    startedAt = Date.now()
    const tokenForm = new URLSearchParams({
      grant_type: "authorization_code",
      client_id: clientId,
      code: callback.searchParams.get("code") ?? "",

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Inspect the authorization server: ensure the /authorize endpoint issues 302 responses with a valid Location header for the request parameters the probe sends (response_type=code, PKCE S256, state, resource).
  2. Remove any proxy or middleware between the probe and the AS that might strip the Location header or return an HTML page.
  3. Check for an HTML login interstitial — the probe performs a synthetic user flow, so the AS must redirect without interactive login.

Example fix

// before (AS handler)
res.status(302).end()
// after
res.status(302).setHeader("location", callbackUrl.href).end()
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await probeEnterpriseMcpMockServer(scenario)
} catch (e) {
  if (e instanceof ProbeFailure && e.code === 'AUTH_USER_OR_WORKLOAD' && e.message.includes('no redirect location')) {
    // inspect the AS /authorize endpoint: log full response headers/status
    console.error('Authorization endpoint did not redirect; check AS 302 + Location behavior', e)
  } else throw e
}

Prevention

When it happens

Trigger: The authorization endpoint returns 302 without a Location header, or returns a success status (handled by expectOk) whose response carries no Location header, leaving `location` null at probe.ts:646-647.

Common situations: A misbehaving or partially-implemented mock/real authorization server; an intermediary (proxy, auth wall, HTML interstitial login page) that breaks the redirect chain; server framework defaulting to an empty redirect.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/a5c4f0f9a8b842df. Report an issue: GitHub.