{"record":{"id":"9a3dadd91cc1437d","repo":"google-gemini/gemini-cli","slug":"extension-is-linked-so-does-not-need-to-be-updated","errorCode":null,"errorMessage":"Extension is linked so does not need to be updated","messagePattern":"Extension is linked so does not need to be updated","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"packages/cli/src/config/extensions/update.ts","lineNumber":80,"sourceCode":"    if (status === IntegrityDataStatus.INVALID) {\n      throw new Error('Extension integrity cannot be verified');\n    }\n  } catch (e) {\n    dispatchExtensionStateUpdate({\n      type: 'SET_STATE',\n      payload: { name: extension.name, state: ExtensionUpdateState.ERROR },\n    });\n    throw new Error(\n      `Extension ${extension.name} cannot be updated. ${getErrorMessage(e)}. To fix this, reinstall the extension.`,\n    );\n  }\n\n  if (installMetadata?.type === 'link') {\n    dispatchExtensionStateUpdate({\n      type: 'SET_STATE',\n      payload: { name: extension.name, state: ExtensionUpdateState.UP_TO_DATE },\n    });\n    throw new Error(`Extension is linked so does not need to be updated`);\n  }\n\n  if (extension.migratedTo) {\n    const migratedState = await checkForExtensionUpdate(\n      {\n        ...extension,\n        installMetadata: { ...installMetadata, source: extension.migratedTo },\n        migratedTo: undefined,\n      },\n      extensionManager,\n    );\n    if (\n      migratedState === ExtensionUpdateState.UPDATE_AVAILABLE ||\n      migratedState === ExtensionUpdateState.UP_TO_DATE\n    ) {\n      installMetadata.source = extension.migratedTo;\n    }\n  }","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/config/extensions/update.ts#L62-L98","documentation":"Thrown by checkForExtensionUpdate when installMetadata.type === 'link'. Linked extensions are local symlinks used during development, so there is no remote source to update from; the code sets the state to UP_TO_DATE and throws to short-circuit the update flow.","triggerScenarios":"Running an update check (batch or single) against an extension that was installed via 'gemini extensions link' / type:'link' rather than cloned or downloaded.","commonSituations":"Developing an extension locally with a symlink and then triggering the updater; a CI image that links extensions for testing and then runs update checks.","solutions":["Treat this as expected: filter linked extensions out of update batches before calling checkForExtensionUpdate.","Update the linked extension by pulling/rebuilding its source directory directly.","Unlink and install normally if you want remote updates."],"exampleFix":"// before\nfor (const ext of extensions) await checkForExtensionUpdate(ext, mgr);\n// after\nfor (const ext of extensions) {\n  const meta = loadInstallMetadata(ext.path);\n  if (meta?.type === 'link') continue;\n  await checkForExtensionUpdate(ext, mgr);\n}","handlingStrategy":"type-guard","validationCode":"const meta = loadInstallMetadata(ext.path);\nif (meta?.type === 'link') { /* skip update check */ }","typeGuard":"function isLinkedExtension(meta?: ExtensionInstallMetadata): boolean { return meta?.type === 'link'; }","tryCatchPattern":"try { await checkForExtensionUpdate(ext, mgr); } catch (e) { if (/linked so does not need/.test(String(e))) { /* expected, ignore */ } else throw e; }","preventionTips":["Filter type:'link' extensions out of update batches.","Update linked extensions from their source directory instead of via the updater."],"tags":["extensions","updates","linked","dev-workflow"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}