{"record":{"id":"e7c54953f33a9e86","repo":"stablyai/orca","slug":"concrete-linear-workspace-id-is-required-e7c549","errorCode":null,"errorMessage":"Concrete Linear workspace ID is required","messagePattern":"Concrete Linear workspace ID is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/linear/projects.ts","lineNumber":534,"sourceCode":"}\n\nfunction coalesce<T>(key: string, load: () => Promise<T>, force = false): Promise<T> {\n  const existing = inFlight.get(key) as Promise<T> | undefined\n  if (existing && !force) {\n    return existing\n  }\n  const promise = load().finally(() => {\n    if (inFlight.get(key) === promise) {\n      inFlight.delete(key)\n    }\n  })\n  inFlight.set(key, promise)\n  return promise\n}\n\nfunction normalizeConcreteWorkspaceId(workspaceId: unknown): LinearConcreteWorkspaceId {\n  if (typeof workspaceId !== 'string' || !workspaceId.trim() || workspaceId === 'all') {\n    throw new Error('Concrete Linear workspace ID is required')\n  }\n  return workspaceId.trim()\n}\n\nfunction workspaceError(entry: LinearClientForWorkspace, error: unknown): LinearWorkspaceError {\n  if (isAuthError(error)) {\n    return {\n      workspaceId: entry.workspace.id,\n      workspaceName: entry.workspace.organizationName,\n      type: 'auth',\n      message: 'Linear authentication expired for this workspace.'\n    }\n  }\n\n  const record = error as { name?: string; message?: string; status?: number; response?: unknown }\n  const message = record.message || 'Linear request failed.'\n  const status =\n    typeof record.status === 'number'","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/linear/projects.ts#L516-L552","documentation":"Thrown by normalizeConcreteWorkspaceId (in projects.ts) when the workspaceId is not a non-empty string or equals 'all'. Project functions take LinearConcreteWorkspaceId, which must be a single real workspace, not the multi-workspace 'all' selector. It is a plain Error, not a LinearWriteFailure.","triggerScenarios":"Passing undefined/null/'all'/'' (or a non-string) to a project function (listProjectsByExactName, getProject, etc.) that calls normalizeConcreteWorkspaceId.","commonSituations":"Caller forwarded a LinearWorkspaceSelection value that can legally be 'all' into a concrete-only API; UI selection defaulted to 'all' and was not narrowed.","solutions":["Resolve to a single workspace id before calling project functions.","If selection is 'all', iterate connected workspaces and call once per concrete id.","Validate the value is a non-empty string other than 'all' at the boundary."],"exampleFix":"// before\nawait listProjectsByExactName(name, workspaceSelection)\n// after\nif (workspaceSelection === 'all') throw new Error('Select one workspace')\nawait listProjectsByExactName(name, workspaceSelection)","handlingStrategy":"validation","validationCode":"function assertConcreteWorkspaceId(workspaceId: unknown): asserts workspaceId is string {\n  if (typeof workspaceId !== 'string' || !workspaceId.trim() || workspaceId === 'all')\n    throw new Error('A concrete Linear workspace id is required')\n}","typeGuard":"function isConcreteWorkspaceId(workspaceId: unknown): workspaceId is string {\n  return typeof workspaceId === 'string' && workspaceId.trim().length > 0 && workspaceId !== 'all'\n}","tryCatchPattern":"try { await listProjectsByExactName(name, workspaceId as LinearConcreteWorkspaceId) }\ncatch (e) { if (e instanceof Error && /concrete.*workspace/i.test(e.message)) { pickConcreteWorkspace(); return } throw e }","preventionTips":["Narrow LinearWorkspaceSelection to a concrete id before project calls.","For 'all' selections, iterate connected workspaces and call per concrete id.","Use the isConcreteWorkspaceId type guard at trust boundaries."],"tags":["linear","validation","workspace","projects"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}