decolua/9router · warning

[ProjectId] could not fetch projectId for connection

Error message

[ProjectId] could not fetch projectId for connection

What it means

getProjectIdForConnection fetches a Google/Vertex-style projectId for an OAuth connection via fetchProjectId. When the fetch succeeds but returns no projectId, it logs this warning, caches nothing, and returns null so callers treat the connection as having no project ID.

Source

Thrown at open-sse/services/projectId.js:110

        return cached.projectId;
    }

    // Deduplicate concurrent fetches for the same connection
    if (pendingFetches.has(connectionId)) {
        return pendingFetches.get(connectionId).promise;
    }

    // Each fetch gets its own AbortController so it can be canceled via removeConnection()
    const controller = new AbortController();

    const promise = (async () => {
        try {
            const projectId = await fetchProjectId(accessToken, controller.signal, provider);
            if (projectId) {
                projectIdCache.set(connectionId, {projectId, fetchedAt: Date.now()});
                return projectId;
            }
            console.warn("[ProjectId] could not fetch projectId for connection", connectionId.slice(0, 8));
            return null;
        } catch (error) {
            console.warn(`[ProjectId] Error fetching project ID: ${error.message}`);
            return null;
        } finally {
            pendingFetches.delete(connectionId);
        }
    })();

    pendingFetches.set(connectionId, {promise, controller, startedAt: Date.now()});
    return promise;
}

/**
 * Invalidate the cached project ID for a connection.
 * Call this when a connection's credentials are fully revoked or refreshed.
 */
export function invalidateProjectId(connectionId) {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Log into the provider console and confirm the account has at least one active project
  2. Re-run the OAuth flow with scopes including project read access
  3. Check fetchProjectId's parsing against the provider's current API response shape
  4. Retry after the account/project is provisioned — cache refresh (_refreshProjectId) will pick it up
  5. If the provider genuinely needs no projectId, treat the null return as expected and skip project-scoped features

Example fix

// before: connection created with minimal scopes
scopes: ["userinfo.email"]
// after: include project read scope
scopes: ["userinfo.email", "cloud-platform"]
Defensive patterns

Strategy: type-guard

Validate before calling

const pid = await getProjectIdForConnection(connectionId);
if (!pid) console.warn('no projectId — skip project-scoped calls');

Type guard

const hasProjectId = (pid) => typeof pid === 'string' && pid.length > 0;
if (!hasProjectId(projectId)) return; // skip project-scoped feature

Try / catch

try {
  const pid = await getProjectIdForConnection(conn.id);
  if (!pid) return null; // function already returns null on failure, never throws
  return await projectScopedCall(conn, pid);
} catch (e) {
  console.warn(`project-scoped call failed: ${e.message}`);
  return null;
}

Prevention

When it happens

Trigger: fetchProjectId returned falsy — the upstream project-listing endpoint returned 200 but with no matching project, the access token lacks permission to list projects, or the provider variant doesn't expose project IDs.

Common situations: OAuth account has no active GCP project; token scopes missing project read permission; provider changed its project discovery API shape so the parser finds nothing; connection created before project setup completed on the provider console.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/e5400d39ddae6220. Report an issue: GitHub.