Infinite Loop Detected, FeatureFlag requests self api route

I recently use feature flags with self implemented generating flag api by nextjs api route handler, because our flag logic to generate doesn’t work edge runtime (it uses node api).
Then my code is mostly similar to one bellow.

  decide({headers, cookies}) {
     const host = headers.get('host')
     const protocol = headers.get('x-forwarded-proto')
     return Boolean(cookies.get('stored-flag'))
     return await fetch(`${protocol}://${host}/api/get-flag`, {
        method: 'POST',...
      }
  }

and

export function POST () {
  ...do something
  return Next.response({flag: true})
}

I create the code to store session cookie to avoid duplicated requests in the middleware.

if(cookies.get('stored-flag'))) { return middleware(request, events)}
const result = await someFlag()
cookes.set('stored-flag')
return middleware(request, events)

It works local environment.

But, at deployed vercel version, my endpoint throws error says Infinite Loop Detected if the requst has user agent ‘Vercel Edge Functions’ and it has _vercel_jwt cookie. What is the request, and why it causes? I also avoid to run middleware in the path starts with ‘/api’ in config value includes ‘/((?!api…’ .

Middleware should avoid Infinite Loop.

import { unstable_flag as flag } from '@vercel/flags/next'

const someFlag = flag({
     key: 'someflag', decide({headers, cookies}) { 
        return cookies.get('someflag') ?? fetch(''api route handler url'', {method: 'POST'})
     } 
})

function POST() {
   return true
}

export const middleware = (request, event) => { 
  if(!request.cookies.get('someflag')) { 
      request.cookies.set('someflag', await someFlag());
  }   
    return  NextResponse.next()  
}

export const config = { matcher:  '/((?!api).*)'}

I noticed the request may be from Feature Flags Tool Bar, because in my environment it’s an only one edge function though I’m not convinced.
It was difficult to identify this request through all inspect to request parameters and headers and cookies. Now, I’m blocking requests from edge to vercel function when it already passed through once vercel functions by checking their user agent Vercel Edge Functions and x-middleware-subrequest.
I’m glad If it can be more clarified in the vercel document.

It sounds like this is what’s happening

  • Your Middleware is set to run for requests like /api/get-flag
  • When the middleware receives a request it uses the feature flag
  • The feature flag then itself makes a new request to /api/get-flag

That’s why the request ends up in an infinite loop.

Aside from that, the logic is trying to set the cookie on the request, but calling NextResponse.next() those modifications made to the request headers will not be forwarded unless explicitly forward. That’s why the new request doesn’t contain the cookie and the application ends up in an infinite loop again.

The docs show how to forward headers when using NextResponse.next:

import { NextResponse } from 'next/server'
 
// Given an incoming request...
const newHeaders = new Headers(request.headers)
// Add a new header
newHeaders.set('x-version', '123')
// And produce a response with the new headers
return NextResponse.next({
  request: {
    // New request headers
    headers: newHeaders,
  },
})

Overall the approach taken here is not advisable since making a blocking request from Edge Middleware to fetch the feature flags increases the latency of the whole application.

Generally the recommended approach here would be to store your feature flag configuration in Edge Config, and to then read the configuration from Edge Middleware. Any additional logic to decide the feature flags should be Edge Middleware compatible. I’d recommend starting to look into fixing the root cause, which is that your flag logic doesn’t work in Edge Middleware.

4 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.