{"record":{"id":"6df997fa89b63a4f","repo":"mastra-ai/mastra","slug":"linear-project-source-id-is-invalid","errorCode":null,"errorMessage":"Linear project source id is invalid.","messagePattern":"Linear project source id is invalid\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/platform/linear/integration.ts","lineNumber":645,"sourceCode":"function parseIssueDetail(issue: LinearIssue, comments: LinearComment[]): IntakeIssueDetail {\n  return {\n    ...parseIssue(issue),\n    commentCount: comments.length,\n    description: issue.description?.trim() ? issue.description : null,\n    comments: comments.map(comment => ({\n      author: comment.user?.displayName ?? comment.user?.name ?? null,\n      body: comment.body,\n      createdAt: comment.createdAt,\n    })),\n  };\n}\n\nfunction encodeSourceId(workspaceId: string, projectId: string): string {\n  return `linear-project:${Buffer.from(JSON.stringify({ workspaceId, projectId })).toString('base64url')}`;\n}\n\nfunction decodeSourceId(sourceId: string): { workspaceId: string; projectId: string } {\n  if (!sourceId.startsWith('linear-project:')) throw new Error('Linear project source id is invalid.');\n  try {\n    const parsed = JSON.parse(Buffer.from(sourceId.slice('linear-project:'.length), 'base64url').toString('utf8')) as {\n      workspaceId?: unknown;\n      projectId?: unknown;\n    };\n    if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n    if (typeof parsed.projectId !== 'string' || !parsed.projectId) throw new Error();\n    return { workspaceId: parsed.workspaceId, projectId: parsed.projectId };\n  } catch {\n    throw new Error('Linear project source id is invalid.');\n  }\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n  return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\nfunction decodeCursor(cursor: string | undefined, sourceIds: string[]): Record<string, string | null | undefined> {","sourceCodeStart":627,"sourceCodeEnd":663,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/platform/linear/integration.ts#L627-L663","documentation":"Cursor/source IDs for Linear projects are encoded as `linear-project:` followed by a base64url JSON payload of `{workspaceId, projectId}`. `decodeSourceId` throws this error when the incoming string lacks the `linear-project:` prefix, or when the decoded payload fails validation (malformed JSON, missing/empty workspaceId or projectId, bad base64url). It guards against persisting or resolving state against a corrupted or foreign source id.","triggerScenarios":"Calling any integration API that decodes a source id with a value that (a) does not start with 'linear-project:', (b) has a suffix that is not valid base64url, (c) decodes to JSON without string workspaceId/projectId. Commonly from a cursor or sourceId loaded from stale storage or hand-crafted.","commonSituations":"Restoring a saved cursor/source id from an older library version with a different encoding; manually constructing source ids instead of using the value returned by the integration; database rows written by another platform's integration (e.g. a github-project id passed to the Linear decoder).","solutions":["Only pass source ids that were produced by `encodeSourceId` (i.e. returned by the integration itself).","If the id came from persisted storage written by an older version, re-derive it by re-fetching the Linear project and re-encoding `{workspaceId, projectId}`.","Check the value for the 'linear-project:' prefix before use to identify cross-platform contamination.","Wrap decode-bearing calls in try/catch and re-sync cursors from scratch when decoding fails."],"exampleFix":"// before\nawait integration.pull({ sourceId: stored.id }); // stored.id = 'github-project:abc'\n// after\nif (!stored.id.startsWith('linear-project:')) {\n  const fresh = await encodeSourceId(workspaceId, projectId);\n}\nawait integration.pull({ sourceId: stored.id.startsWith('linear-project:') ? stored.id : fresh });","handlingStrategy":"type-guard","validationCode":"function isLinearProjectSourceId(id: unknown): id is string {\n  if (typeof id !== 'string' || !id.startsWith('linear-project:')) return false;\n  try {\n    const p = JSON.parse(Buffer.from(id.slice('linear-project:'.length), 'base64url').toString('utf8'));\n    return typeof p?.workspaceId === 'string' && p.workspaceId.length > 0 &&\n           typeof p?.projectId === 'string' && p.projectId.length > 0;\n  } catch { return false; }\n}","typeGuard":"function isLinearProjectSourceId(id: unknown): id is string {\n  return typeof id === 'string' && id.startsWith('linear-project:') &&\n    (() => { try {\n      const p = JSON.parse(Buffer.from(id.slice(15), 'base64url').toString('utf8'));\n      return typeof p.workspaceId === 'string' && !!p.workspaceId && typeof p.projectId === 'string' && !!p.projectId;\n    } catch { return false; } })();\n}","tryCatchPattern":"try {\n  await integration.pull({ sourceId });\n} catch (err) {\n  if (err instanceof Error && err.message === 'Linear project source id is invalid.') {\n    // re-encode or re-sync from scratch\n  } else throw err;\n}","preventionTips":["Always use source ids returned by the integration, never hand-built strings.","Tag persisted source ids with the integration kind so cross-platform ids cannot be mixed.","Validate stored ids on load with the prefix check before use."],"tags":["linear","parsing","validation","cursor"],"backgroundTag":"invalid-cursor-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}