- {activeLinearIssueError ? (
+ {linearLoginUnreadable ? (
+
+
+
+ {translate('auto.components.TaskPage.linearLoginUnreadableTitle', 'Reconnect Linear')}
+
+
+ {translate(
+ 'auto.components.TaskPage.linearLoginUnreadableBody',
+ 'Unable to read saved Linear login. Reconnect it in Settings.'
+ )}
+
+
+ ) : activeLinearIssueError ? (
{activeLinearIssueError}
) : null}
- {activeLinearIssueLoading && activeLinearIssues.length === 0 ? (
+ {activeLinearIssueLoading && activeLinearIssues.length === 0 && !linearLoginUnreadable ? (
{Array.from({
length: 12
diff --git a/src/renderer/src/components/use-task-page-linear-list-effects.ts b/src/renderer/src/components/use-task-page-linear-list-effects.ts
index 8409e8682e4..f5d47b1b7f6 100644
--- a/src/renderer/src/components/use-task-page-linear-list-effects.ts
+++ b/src/renderer/src/components/use-task-page-linear-list-effects.ts
@@ -1,5 +1,6 @@
import type { TaskPageGlobalEffectsModel } from './use-task-page-global-effects'
import { useEffect } from 'react'
+import { isIntegrationCredentialDecryptionError } from '../../../shared/integration-credential-errors'
import { saveLinearIssueView } from './linear-issue-view-storage'
import { serializeLinearIssueViewResumeState } from '../../../shared/linear/issue-view-resume-state'
import {
@@ -139,7 +140,10 @@ export function useTaskPageLinearListEffects(model: TaskPageGlobalEffectsModel)
return
}
let cancelled = false
- setLinearError(null)
+ // Why: keep an unreadable-login error up during the re-read so the Reconnect card doesn't flash.
+ setLinearError((current) =>
+ current && isIntegrationCredentialDecryptionError(current) ? current : null
+ )
const trimmed = appliedLinearSearch.trim()
const effectiveLinearIssueLimit = clampLinearIssueListLimit(linearIssueLimit)
const searchActive = trimmed.length > 0
@@ -233,6 +237,7 @@ export function useTaskPageLinearListEffects(model: TaskPageGlobalEffectsModel)
) {
return
}
+ setLinearError(null)
if (readArgs.kind === 'search') {
const issues = result as LinearIssue[]
setLinearIssuesHasMore(false)
@@ -264,6 +269,11 @@ export function useTaskPageLinearListEffects(model: TaskPageGlobalEffectsModel)
) {
return
}
+ // Why: a credential failure used to resolve as no rows; keep not showing the previous scope's rows.
+ if (isIntegrationCredentialDecryptionError(err)) {
+ setLinearIssues([])
+ setLinearIssuesHasMore(false)
+ }
setLinearError(err instanceof Error ? err.message : 'Failed to load Linear issues.')
setLinearLoading(false)
})
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index b629dda8b19..b8081b560af 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -2357,6 +2357,8 @@
"851017590d": "Add Linear access",
"228b25028f": "Browse and start work on your assigned Linear issues directly from here.",
"6d56559467": "Connect your Linear account",
+ "linearLoginUnreadableTitle": "Reconnect Linear",
+ "linearLoginUnreadableBody": "Unable to read saved Linear login. Reconnect it in Settings.",
"eee68073b2": "Open in Jira",
"9497f2787c": "Start workspace",
"eba87f2edb": "No Jira issues found",
@@ -3116,6 +3118,7 @@
"93e1f6bfca": "Loading",
"98730088a6": ". Search or open Linear for the full set.",
"06b887d622": "Showing first",
+ "linearLoginUnreadable": "Unable to read saved Linear login. Reconnect it in Settings.",
"2c4b1c2c08": "number",
"f2cc1e0ff6": "Linear / Projects",
"906b5e4cb8": "Linear / Projects / {{value0}}",
@@ -14781,7 +14784,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "Couldn’t link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji",
- "loadingLinearIssue": "Loading Linear issue…"
+ "loadingLinearIssue": "Loading Linear issue…",
+ "linearLoginUnreadableTitle": "Reconnect Linear",
+ "linearLoginUnreadableBody": "Unable to read saved Linear login. Reconnect it in Settings."
},
"ProjectCombobox": {
"empty": "No projects match your search.",
diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json
index 66984e2f08e..9b62de6f936 100644
--- a/src/renderer/src/i18n/locales/es.json
+++ b/src/renderer/src/i18n/locales/es.json
@@ -2128,6 +2128,8 @@
"851017590d": "Agregar acceso Linear",
"228b25028f": "Explora y empieza a trabajar en tus issues asignados de Linear directamente desde aquí.",
"6d56559467": "Conecta tu cuenta Linear",
+ "linearLoginUnreadableTitle": "Vuelve a conectar Linear",
+ "linearLoginUnreadableBody": "No se puede leer el inicio de sesión guardado de Linear. Vuelve a conectarlo en Configuración.",
"eee68073b2": "Abrir en Jira",
"9497f2787c": "Iniciar espacio de trabajo",
"eba87f2edb": "No se encontraron issues de Jira",
@@ -2887,6 +2889,7 @@
"93e1f6bfca": "Cargando",
"98730088a6": ". Busca o abre Linear para ver el conjunto completo.",
"06b887d622": "Mostrando primero",
+ "linearLoginUnreadable": "No se puede leer el inicio de sesión guardado de Linear. Vuelve a conectarlo en Configuración.",
"2c4b1c2c08": "número",
"f2cc1e0ff6": "Linear / Proyectos",
"906b5e4cb8": "Linear / Proyectos / {{value0}}",
@@ -14491,7 +14494,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "Couldn’t link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji",
- "loadingLinearIssue": "Cargando problema Linear..."
+ "loadingLinearIssue": "Cargando problema Linear...",
+ "linearLoginUnreadableTitle": "Vuelve a conectar Linear",
+ "linearLoginUnreadableBody": "No se puede leer el inicio de sesión guardado de Linear. Vuelve a conectarlo en Configuración."
},
"ProjectCombobox": {
"empty": "Ningún proyecto coincide con tu búsqueda.",
diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json
index 891dbdc2fd3..53e368f987b 100644
--- a/src/renderer/src/i18n/locales/fr.json
+++ b/src/renderer/src/i18n/locales/fr.json
@@ -2237,6 +2237,8 @@
"851017590d": "Ajouter l'accès Linear",
"228b25028f": "Parcourez vos tickets Linear assignés et démarrez le travail directement depuis ici.",
"6d56559467": "Connectez votre compte Linear",
+ "linearLoginUnreadableTitle": "Reconnectez Linear",
+ "linearLoginUnreadableBody": "Impossible de lire la connexion Linear enregistrée. Reconnectez-la dans les réglages.",
"eee68073b2": "Ouvrir dans Jira",
"9497f2787c": "Démarrer l'espace de travail",
"eba87f2edb": "Aucun ticket Jira trouvé",
@@ -2996,6 +2998,7 @@
"93e1f6bfca": "Chargement",
"98730088a6": ". Recherchez ou ouvrez Linear pour voir l'ensemble complet.",
"06b887d622": "Affichage des premières",
+ "linearLoginUnreadable": "Impossible de lire la connexion Linear enregistrée. Reconnectez-la dans les réglages.",
"2c4b1c2c08": "nombre",
"f2cc1e0ff6": "Linear / Projets",
"906b5e4cb8": "Linear / Projets / {{value0}}",
@@ -14613,7 +14616,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "Impossible d'associer cette issue Jira. Sélectionnez le site correspondant ou reconnectez Jira, puis réessayez.",
"emoji": "Émoji",
- "loadingLinearIssue": "Chargement de l'issue Linear…"
+ "loadingLinearIssue": "Chargement de l'issue Linear…",
+ "linearLoginUnreadableTitle": "Reconnectez Linear",
+ "linearLoginUnreadableBody": "Impossible de lire la connexion Linear enregistrée. Reconnectez-la dans les réglages."
},
"ProjectCombobox": {
"empty": "Aucun projet ne correspond à votre recherche.",
diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json
index fa1e856a70e..214a8e8d238 100644
--- a/src/renderer/src/i18n/locales/ja.json
+++ b/src/renderer/src/i18n/locales/ja.json
@@ -2129,6 +2129,8 @@
"851017590d": "Linear アクセスを追加",
"228b25028f": "ここから直接、割り当てられた Linear Issue を参照して作業を開始できます。",
"6d56559467": "Linear アカウントを接続する",
+ "linearLoginUnreadableTitle": "Linear を再接続",
+ "linearLoginUnreadableBody": "保存された Linear のログイン情報を読み取れません。設定で再接続してください。",
"eee68073b2": "Jira で開く",
"9497f2787c": "ワークスペースを開始",
"eba87f2edb": "Jira Issue が見つかりません",
@@ -2888,6 +2890,7 @@
"93e1f6bfca": "読み込み中",
"98730088a6": "。フルセットについては、Linear を検索するか開きます。",
"06b887d622": "最初に表示しています",
+ "linearLoginUnreadable": "保存された Linear のログイン情報を読み取れません。設定で再接続してください。",
"2c4b1c2c08": "番号",
"f2cc1e0ff6": "Linear / プロジェクト",
"906b5e4cb8": "Linear / プロジェクト / {{value0}}",
@@ -14496,7 +14499,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "この Jira Issue をリンクできませんでした。該当するサイトを選択するか Jira に再接続してから、もう一度お試しください。",
"emoji": "絵文字",
- "loadingLinearIssue": "Linear Issue を読み込み中…"
+ "loadingLinearIssue": "Linear Issue を読み込み中…",
+ "linearLoginUnreadableTitle": "Linear を再接続",
+ "linearLoginUnreadableBody": "保存された Linear のログイン情報を読み取れません。設定で再接続してください。"
},
"ProjectCombobox": {
"empty": "検索条件に一致するプロジェクトはありません。",
diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json
index e31dcae3432..28985b0f21d 100644
--- a/src/renderer/src/i18n/locales/ko.json
+++ b/src/renderer/src/i18n/locales/ko.json
@@ -2129,6 +2129,8 @@
"851017590d": "Linear 액세스 추가",
"228b25028f": "여기에서 직접 할당된 Linear 이슈를 찾아보고 작업을 시작하세요.",
"6d56559467": "Linear 계정 연결",
+ "linearLoginUnreadableTitle": "Linear 다시 연결",
+ "linearLoginUnreadableBody": "저장된 Linear 로그인을 읽을 수 없습니다. 설정에서 다시 연결하세요.",
"eee68073b2": "Jira에서 열기",
"9497f2787c": "워크스페이스 시작",
"eba87f2edb": "Jira 이슈가 발견되지 않았습니다.",
@@ -2888,6 +2890,7 @@
"93e1f6bfca": "로드 중",
"98730088a6": ". 전체 세트를 보려면 Linear를 검색하거나 엽니다.",
"06b887d622": "처음 표시",
+ "linearLoginUnreadable": "저장된 Linear 로그인을 읽을 수 없습니다. 설정에서 다시 연결하세요.",
"2c4b1c2c08": "숫자",
"f2cc1e0ff6": "Linear / 프로젝트",
"906b5e4cb8": "Linear / 프로젝트 / {{value0}}",
@@ -14496,7 +14499,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "Couldn’t link this Jira issue. Pick the matching site or reconnect Jira, then try again.",
"emoji": "Emoji",
- "loadingLinearIssue": "Linear 문제 로드 중…"
+ "loadingLinearIssue": "Linear 문제 로드 중…",
+ "linearLoginUnreadableTitle": "Linear 다시 연결",
+ "linearLoginUnreadableBody": "저장된 Linear 로그인을 읽을 수 없습니다. 설정에서 다시 연결하세요."
},
"ProjectCombobox": {
"empty": "검색과 일치하는 프로젝트가 없습니다.",
diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json
index 8fa4a070565..f009c611419 100644
--- a/src/renderer/src/i18n/locales/zh.json
+++ b/src/renderer/src/i18n/locales/zh.json
@@ -2129,6 +2129,8 @@
"851017590d": "添加 Linear 访问",
"228b25028f": "直接从这里浏览并开始处理分配给您的 Linear 议题。",
"6d56559467": "连接您的 Linear 账户",
+ "linearLoginUnreadableTitle": "重新连接 Linear",
+ "linearLoginUnreadableBody": "无法读取已保存的 Linear 登录信息。请在设置中重新连接。",
"eee68073b2": "在 Jira 中打开",
"9497f2787c": "启动工作区",
"eba87f2edb": "未发现 Jira 议题",
@@ -2888,6 +2890,7 @@
"93e1f6bfca": "加载中",
"98730088a6": "。在 Linear 中搜索或打开以查看完整列表。",
"06b887d622": "首先显示",
+ "linearLoginUnreadable": "无法读取已保存的 Linear 登录信息。请在设置中重新连接。",
"2c4b1c2c08": "数字",
"f2cc1e0ff6": "Linear/项目",
"906b5e4cb8": "Linear/项目/{{value0}}",
@@ -14496,7 +14499,9 @@
"jiraMode": "Jira",
"jiraSelectBindFailed": "无法关联此 Jira 议题。请选择匹配的站点或重新连接 Jira,然后重试。",
"emoji": "Emoji",
- "loadingLinearIssue": "正在加载 Linear 议题..."
+ "loadingLinearIssue": "正在加载 Linear 议题...",
+ "linearLoginUnreadableTitle": "重新连接 Linear",
+ "linearLoginUnreadableBody": "无法读取已保存的 Linear 登录信息。请在设置中重新连接。"
},
"ProjectCombobox": {
"empty": "没有匹配搜索的项目。",
diff --git a/src/renderer/src/lib/linear-issue-url-lookup.ts b/src/renderer/src/lib/linear-issue-url-lookup.ts
index 14f76da309a..e67e465f98f 100644
--- a/src/renderer/src/lib/linear-issue-url-lookup.ts
+++ b/src/renderer/src/lib/linear-issue-url-lookup.ts
@@ -1,4 +1,8 @@
import { linearStatus } from '@/runtime/runtime-linear-client'
+import {
+ credentialDecryptionMessage,
+ isIntegrationCredentialDecryptionError
+} from '../../../shared/integration-credential-errors'
import {
findLinearIssueWorkspaceLookupIds,
isLinearIssueUrlResolutionMatch,
@@ -23,7 +27,10 @@ async function fetchMatchingLinearIssue(
try {
const issue = await fetchLinearIssue(intent.identifier, workspaceId, { sourceContext })
return issue && isLinearIssueUrlResolutionMatch(intent, issue) ? issue : null
- } catch {
+ } catch (error) {
+ if (isIntegrationCredentialDecryptionError(error)) {
+ throw error
+ }
return null
}
}
@@ -46,6 +53,7 @@ export async function lookupLinearIssueUrl({
readLinearStatus?: (sourceContext: TaskSourceContext | null) => Promise
}): Promise {
const triedWorkspaceIds = new Set()
+ const undecryptableWorkspaceIds = new Set()
const lookupInStatus = async (
status: Pick<
LinearConnectionStatus,
@@ -62,7 +70,10 @@ export async function lookupLinearIssueUrl({
workspaceId,
sourceContext,
fetchLinearIssue
- )
+ ).catch(() => {
+ undecryptableWorkspaceIds.add(workspaceId)
+ return null
+ })
if (issue) {
return issue
}
@@ -76,5 +87,10 @@ export async function lookupLinearIssueUrl({
}
const currentStatus = await readLinearStatus(sourceContext).catch(() => null)
- return currentStatus ? lookupInStatus(currentStatus) : null
+ const issue = currentStatus ? await lookupInStatus(currentStatus) : null
+ // Why: a healthy workspace may still match; report the credential failure only when none did.
+ if (!issue && undecryptableWorkspaceIds.size > 0) {
+ throw new Error(credentialDecryptionMessage('Linear'))
+ }
+ return issue
}
diff --git a/src/renderer/src/store/slices/linear-credential-error-recovery.test.ts b/src/renderer/src/store/slices/linear-credential-error-recovery.test.ts
index 1644e42361a..e0f08403eac 100644
--- a/src/renderer/src/store/slices/linear-credential-error-recovery.test.ts
+++ b/src/renderer/src/store/slices/linear-credential-error-recovery.test.ts
@@ -65,42 +65,25 @@ describe('createLinearSlice caching', () => {
vi.clearAllMocks()
})
- it('returns an empty list and refreshes status on Linear decrypt errors during list reads', async () => {
+ it('rejects Linear search and list reads with the plain decrypt message and refreshes status', async () => {
const store = createTestStore()
- const error = new Error(credentialDecryptionMessage('Linear'))
+ const message = credentialDecryptionMessage('Linear')
+ // Local reads arrive through IPC, which prefixes the main-process message.
+ const error = new Error(`Error invoking remote method 'linear:searchIssues': Error: ${message}`)
store.setState({
linearStatus: { connected: true, viewer: null, selectedWorkspaceId: 'workspace-1' },
linearListCache: {
'workspace-1::list::all::36::': { data: { items: [issue('LIN-CACHED')] }, fetchedAt: 1 }
}
})
- linearStatus.mockResolvedValue({
- connected: true,
- viewer: null,
- credentialError: error.message
- })
+ linearStatus.mockResolvedValue({ connected: true, viewer: null, credentialError: message })
+ linearSearchIssues.mockRejectedValueOnce(error)
linearListIssues.mockRejectedValueOnce(error)
+ await expect(store.getState().searchLinearIssues('bug', 36)).rejects.toThrow(new Error(message))
await expect(
store.getState().listLinearIssues({ kind: 'list', filter: 'all', limit: 36 }, { force: true })
- ).resolves.toMatchObject({ items: [] })
- expect(linearStatus).toHaveBeenCalled()
- })
-
- it('returns an empty list and refreshes status on Linear decrypt errors during searches', async () => {
- const store = createTestStore()
- const error = new Error(credentialDecryptionMessage('Linear'))
- store.setState({
- linearStatus: { connected: true, viewer: null, selectedWorkspaceId: 'workspace-1' }
- })
- linearStatus.mockResolvedValue({
- connected: true,
- viewer: null,
- credentialError: error.message
- })
- linearSearchIssues.mockRejectedValueOnce(error)
-
- await expect(store.getState().searchLinearIssues('bug', 36)).resolves.toEqual([])
+ ).rejects.toThrow(new Error(message))
expect(linearStatus).toHaveBeenCalled()
})
diff --git a/src/renderer/src/store/slices/linear/linear-issue-detail-actions.ts b/src/renderer/src/store/slices/linear/linear-issue-detail-actions.ts
index f2826509f45..1c319992bca 100644
--- a/src/renderer/src/store/slices/linear/linear-issue-detail-actions.ts
+++ b/src/renderer/src/store/slices/linear/linear-issue-detail-actions.ts
@@ -5,7 +5,10 @@ import type {
LinearSliceSet,
LinearFetchOptions
} from './linear-slice-contract'
-import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors'
+import {
+ credentialDecryptionMessage,
+ isIntegrationCredentialDecryptionError
+} from '../../../../../shared/integration-credential-errors'
import { linearGetIssue } from '@/runtime/runtime-linear-issue-mutations'
import {
clearLinearIssueCollectionRequestMaps,
@@ -45,6 +48,10 @@ export function createLinearIssueDetailActions(
if (isFresh(cached)) {
return cached.data
}
+ // Why: cards and sub-issue reads treat an unreadable issue as missing; only the
+ // composer's URL lookup asks to see the credential failure.
+ const settle = (request: Promise): Promise =>
+ options?.rejectCredentialError ? request : request.catch(() => null)
const inflight = inflightIssueRequests.get(issueCacheKey)
if (
@@ -52,7 +59,7 @@ export function createLinearIssueDetailActions(
inflight.contextKey === contextKey &&
inflight.mutationGeneration === getLinearMutationGeneration()
) {
- return inflight.promise
+ return settle(inflight.promise)
}
let entry: InflightLinearIssueRequest
@@ -94,6 +101,9 @@ export function createLinearIssueDetailActions(
) {
void get().checkLinearConnection(true)
}
+ if (isIntegrationCredentialDecryptionError(error)) {
+ throw new Error(credentialDecryptionMessage('Linear'))
+ }
return null
})
.finally(() => {
@@ -121,7 +131,7 @@ export function createLinearIssueDetailActions(
mutationGeneration: requestMutationGeneration
}
inflightIssueRequests.set(issueCacheKey, entry)
- return promise
+ return settle(promise)
},
refreshLinearIssue: async (
diff --git a/src/renderer/src/store/slices/linear/linear-issue-list-actions.ts b/src/renderer/src/store/slices/linear/linear-issue-list-actions.ts
index 8c715265ae8..a6fe4116af8 100644
--- a/src/renderer/src/store/slices/linear/linear-issue-list-actions.ts
+++ b/src/renderer/src/store/slices/linear/linear-issue-list-actions.ts
@@ -8,7 +8,10 @@ import type {
LinearSliceSet
} from './linear-slice-contract'
import { clampLinearIssueListLimit } from '../../../../../shared/linear/issue-read-limits'
-import { isIntegrationCredentialDecryptionError } from '../../../../../shared/integration-credential-errors'
+import {
+ credentialDecryptionMessage,
+ isIntegrationCredentialDecryptionError
+} from '../../../../../shared/integration-credential-errors'
import {
isLinearIssueAttributeFilterUnsupportedError,
linearListIssues,
@@ -91,9 +94,10 @@ export function createLinearIssueListActions(
})
.catch((error) => {
console.warn('[linear] searchLinearIssues failed:', error)
+ const credentialError = isIntegrationCredentialDecryptionError(error)
if (
!(scope.explicitSource && options?.workspaceId !== undefined) &&
- (isIntegrationCredentialDecryptionError(error) || looksLikeAuthError(error)) &&
+ (credentialError || looksLikeAuthError(error)) &&
canWriteLinearReadResult(
contextKey,
requestCacheGeneration,
@@ -105,7 +109,13 @@ export function createLinearIssueListActions(
if (!shouldRefreshStatusAfterRead(workspaceId, get().linearStatus)) {
void get().checkLinearConnection(true)
}
- return []
+ if (!credentialError) {
+ return []
+ }
+ }
+ // Why: [] would read as "no matches"; the caller shows this read's own error.
+ if (credentialError) {
+ throw new Error(credentialDecryptionMessage('Linear'))
}
return get().linearSearchCache[cacheKey]?.data ?? []
})
@@ -203,9 +213,10 @@ export function createLinearIssueListActions(
if (isLinearIssueAttributeFilterUnsupportedError(error)) {
throw error
}
+ const credentialError = isIntegrationCredentialDecryptionError(error)
if (
!(scope.explicitSource && options?.workspaceId !== undefined) &&
- (isIntegrationCredentialDecryptionError(error) || looksLikeAuthError(error)) &&
+ (credentialError || looksLikeAuthError(error)) &&
canWriteLinearReadResult(
contextKey,
requestCacheGeneration,
@@ -217,7 +228,12 @@ export function createLinearIssueListActions(
if (!shouldRefreshStatusAfterRead(workspaceId, get().linearStatus)) {
void get().checkLinearConnection(true)
}
- return emptyLinearCollection()
+ if (!credentialError) {
+ return emptyLinearCollection()
+ }
+ }
+ if (credentialError) {
+ throw new Error(credentialDecryptionMessage('Linear'))
}
return get().linearListCache[cacheKey]?.data ?? emptyLinearCollection()
})
diff --git a/src/renderer/src/store/slices/linear/linear-slice-contract.ts b/src/renderer/src/store/slices/linear/linear-slice-contract.ts
index aff0802c99f..e16e5cdbaf1 100644
--- a/src/renderer/src/store/slices/linear/linear-slice-contract.ts
+++ b/src/renderer/src/store/slices/linear/linear-slice-contract.ts
@@ -33,6 +33,8 @@ export type LinearFetchOptions = {
force?: boolean
sourceContext?: TaskSourceContext | null
workspaceId?: LinearWorkspaceSelection | null
+ // Issue reads only: reject on an undecryptable credential instead of resolving null.
+ rejectCredentialError?: boolean
}
export type LinearPatchOptions = { sourceContext?: TaskSourceContext | null }