{"record":{"id":"af2c7787bdbc2de8","repo":"can1357/oh-my-pi","slug":"cursor-api-key-access-token-is-required","errorCode":null,"errorMessage":"Cursor API key (access token) is required","messagePattern":"Cursor API key \\(access token\\) is required","errorType":"exception","errorClass":"AIError.MissingApiKeyError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/cursor.ts","lineNumber":681,"sourceCode":"\t\t\t\t\tnew AIError.ProviderResponseError(\"Cursor stream ended before turnEnded\", {\n\t\t\t\t\t\tkind: \"incomplete-stream\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\th2Completion.resolve();\n\t\t};\n\n\t\t// Hoisted out of the try block: the #8345 rotation in the catch path\n\t\t// needs both ids, and the catch block cannot see try-scoped consts.\n\t\tlet baseConversationId: string | undefined;\n\t\tlet conversationId: string | undefined;\n\t\tlet usageState: UsageState | undefined;\n\t\tlet serializedFallbackWireModelId: string | undefined;\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new AIError.MissingApiKeyError(undefined, \"Cursor API key (access token) is required\");\n\t\t\t}\n\n\t\t\tbaseConversationId = options?.conversationId ?? options?.sessionId ?? crypto.randomUUID();\n\t\t\tconversationId = rotatedConversationIds.get(baseConversationId) ?? baseConversationId;\n\t\t\tconst rotatedFresh = freshRotatedConversationIds.has(conversationId);\n\t\t\tconst blobStore = conversationBlobStores.get(conversationId) ?? new Map<string, Uint8Array>();\n\t\t\tconversationBlobStores.set(conversationId, blobStore);\n\t\t\tconst cachedState = rotatedFresh ? undefined : conversationStateCache.get(conversationId);\n\t\t\tconst builtRequest = await buildGrpcRequestForWireMode(\n\t\t\t\tmodel,\n\t\t\t\tcontext,\n\t\t\t\toptions,\n\t\t\t\t{\n\t\t\t\t\tconversationId,\n\t\t\t\t\tblobStore,\n\t\t\t\t\tconversationState: cachedState,\n\t\t\t\t\trotatedFresh,\n\t\t\t\t},","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/cursor.ts#L663-L699","documentation":"The Cursor provider authenticates with an API key (Cursor access token) supplied via options.apiKey. streamCursorWithWireMode checks for the key up front and throws MissingApiKeyError when it is absent, before any network traffic. Cursor has no ambient env fallback in this path, so the key must be provided by the caller.","triggerScenarios":"Calling streamCursor or the wire-mode streaming path without options.apiKey (or with an empty string), e.g. streaming a cursor/* model with only a model id and context.","commonSituations":"Cursor access token expired/rotated and removed from config; running in an environment where the credential store isn't populated; forgetting to pass the key after switching from a provider that reads env vars automatically.","solutions":["Pass the access token: streamCursor(model, ctx, { apiKey: \"<cursor-access-token>\" }).","Generate a new access token from Cursor settings if the old one was revoked, then supply it.","Load the key from your app's config/credential store and inject it into options before calling.","Add a startup check that fails fast with a clear message when the Cursor key is missing."],"exampleFix":"// before\nawait streamCursor(model, context); // no apiKey\n// after\nawait streamCursor(model, context, {\n  apiKey: process.env.CURSOR_API_KEY,\n});","handlingStrategy":"validation","validationCode":"const apiKey = options?.apiKey ?? config.cursorAccessToken;\nif (!apiKey) throw new Error(\"Cursor API key (access token) must be supplied via options.apiKey before streaming.\");","typeGuard":"null","tryCatchPattern":"try {\n  await streamCursor(model, ctx, options);\n} catch (err) {\n  if (err instanceof AIError.MissingApiKeyError && err.message.includes(\"Cursor\")) {\n    // prompt for the access token or redirect to credential setup\n  } else throw err;\n}","preventionTips":["Check for the Cursor access token during app startup.","Store the token in your credential store and refresh it when rotated.","Never assume an env fallback exists for this provider — pass the key explicitly."],"tags":["authentication","api-key","cursor","missing-credential"],"backgroundTag":"missing-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}