{"record":{"id":"14516e8225ff599d","repo":"different-ai/openwork","slug":"opencode-client-unavailable","errorCode":null,"errorMessage":"OpenCode client unavailable.","messagePattern":"OpenCode client unavailable\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/react-app/domains/settings/state/extensions-store.ts","lineNumber":1528,"sourceCode":"    if (!client) {\n      mutateState((current) => ({\n        ...current,\n        skills: [],\n        skillsStatus: \"OpenWork server unavailable. Connect to load skills.\",\n      }));\n      return;\n    }\n\n    if (root !== skillsRoot) skillsLoaded = false;\n    if (!optionsOverride?.force && skillsLoaded) return;\n    if (refreshSkillsInFlight) return;\n\n    refreshSkillsInFlight = true;\n    refreshSkillsAborted = false;\n    try {\n      setStateField(\"skillsStatus\", null);\n      const rawClient = client as unknown as { _client?: { get: (input: { url: string }) => Promise<unknown> } };\n      if (!rawClient._client) throw new Error(\"OpenCode client unavailable.\");\n      const result = await rawClient._client.get({ url: \"/skill\" }) as {\n        data?: Array<{ name: string; description: string; location: string }>;\n        error?: unknown;\n      };\n      if (result?.data === undefined) {\n        const err = result?.error;\n        const message = err instanceof Error ? err.message : typeof err === \"string\" ? err : t(\"skills.failed_to_load\");\n        throw new Error(message);\n      }\n      if (refreshSkillsAborted) return;\n      const next: SkillCard[] = Array.isArray(result.data)\n        ? result.data.map((entry) => ({\n            name: entry.name,\n            description: entry.description,\n            path: formatSkillPath(entry.location),\n          }))\n        : [];\n      mutateState((current) => ({","sourceCodeStart":1510,"sourceCodeEnd":1546,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/react-app/domains/settings/state/extensions-store.ts#L1510-L1546","documentation":"refreshSkills loads the workspace skill list by calling the underlying OpenCode HTTP client. The public client object is probed for the private _client handle that exposes raw .get(); if that internal handle is missing, the raw /skill request cannot be made and this error is thrown.","triggerScenarios":"Calling refreshSkills (e.g. via installClaudePlugin's refreshSkills({force:true}) or initial store load) when the injected OpenCode client instance lacks the internal _client property — i.e. a client wrapper/version whose internals differ, or client is null/wrong shape.","commonSituations":"OpenCode SDK was upgraded and the internal `_client` field was renamed or removed; the store was constructed with a mock or different client implementation in tests; the client failed to initialize and an incomplete object was passed in.","solutions":["Pin/restore the OpenCode client version whose client exposes the `_client` handle, or update this store to use the new public API surface.","Initialize the OpenCode client properly before constructing the extensions store and pass the real instance.","For tests, build the mock with a `_client: { get: async () => ({ data: [...] }) }` field to satisfy the probe.","Wrap the call so this error surfaces as 'skills unavailable' rather than a raw crash, and skip the refresh when the client is unusable."],"exampleFix":"// before\nconst rawClient = client as unknown as { _client?: { get: ... } };\nif (!rawClient._client) throw new Error(\"OpenCode client unavailable.\");\n// after — defensive caller check\nfunction canFetchSkills(client: unknown): client is { _client: { get: (i: { url: string }) => Promise<unknown> } } {\n  return !!client && typeof client === \"object\" && \"_client\" in client &&\n    typeof (client as { _client?: { get?: unknown } })._client?.get === \"function\";\n}","handlingStrategy":"type-guard","validationCode":"function canFetchSkills(client: unknown): boolean {\n  const c = client as { _client?: { get?: unknown } } | null;\n  return !!c && !!c._client && typeof c._client.get === \"function\";\n}","typeGuard":"function hasRawClient(client: unknown): client is { _client: { get: (i: { url: string }) => Promise<unknown> } } {\n  const c = client as { _client?: { get?: unknown } };\n  return !!c._client && typeof c._client.get === \"function\";\n}","tryCatchPattern":"try {\n  await store.refreshSkills({ force: true });\n} catch (e) {\n  if (e instanceof Error && e.message === 'OpenCode client unavailable.') {\n    ui.showSkillsUnavailable(); // do not retry until client re-initialized\n  } else throw e;\n}","preventionTips":["Pin the OpenCode SDK version tested against the store","Re-initialize the client after SDK upgrades","Type the client contract explicitly instead of probing internals","Cover client shape in tests"],"tags":["client","skills","internal-api"],"backgroundTag":"client-not-initialized","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}