{"record":{"id":"2480c749b9d4ece8","repo":"nexu-io/open-design","slug":"connector-refresh-source-requires-connector-metada","errorCode":null,"errorMessage":"connector refresh source requires connector metadata","messagePattern":"connector refresh source requires connector metadata","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/refresh-service.ts","lineNumber":87,"sourceCode":"function isSupportedSource(source: LiveArtifactSource | undefined): source is LiveArtifactSource {\n  if (source === undefined) return false;\n  return source.type === 'local_file' || source.type === 'daemon_tool' || source.type === 'connector_tool';\n}\n\nfunction hasRefreshPermission(source: LiveArtifactSource): boolean {\n  return source.refreshPermission === 'manual_refresh_granted_for_read_only';\n}\n\nasync function executeRefreshSource(options: {\n  projectsRoot: string;\n  projectId: string;\n  source: LiveArtifactSource;\n  signal: AbortSignal;\n}): Promise<BoundedJsonObject> {\n  const { projectsRoot, projectId, source, signal } = options;\n  if (source.type === 'connector_tool') {\n    const connector = source.connector;\n    if (connector === undefined) throw new Error('connector refresh source requires connector metadata');\n    const result = await connectorService.execute(\n      {\n        connectorId: connector.connectorId,\n        toolName: connector.toolName,\n        input: source.input,\n        ...(connector.accountLabel === undefined ? {} : { expectedAccountLabel: connector.accountLabel }),\n      },\n      { projectsRoot, projectId, purpose: 'artifact_refresh', signal },\n    );\n    if (result.output === null || typeof result.output !== 'object' || Array.isArray(result.output)) {\n      throw new Error('connector refresh output must be a JSON object');\n    }\n    return result.output;\n  }\n  if (source.type !== 'daemon_tool' && source.type !== 'local_file') {\n    throw new Error(`refresh source ${source.type} is not supported yet`);\n  }\n  return executeLocalDaemonRefreshSource({ projectsRoot, projectId, source, signal });","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/refresh-service.ts#L69-L105","documentation":"Raised by executeRefreshSource() when source.type === 'connector_tool' but source.connector is undefined. Per the LiveArtifactSource contract, a connector_tool source must carry connector metadata (connectorId, toolName, optional accountLabel) so the refresh knows which connector tool to re-execute; the absence means the artifact's data.json is structurally invalid.","triggerScenarios":"Refreshing a live artifact whose stored LiveArtifactSource has type 'connector_tool' but no connector object — e.g. it was created by an older writer, a migration dropped the field, or a tool produced a source JSON missing the connector block.","commonSituations":"Schema migration that did not backfill connector metadata on existing connector_tool artifacts; a custom tool that wrote data.json without the connector block; partial write interrupted before connector was attached; connector disconnected and its metadata stripped from the source.","solutions":["Before refreshing, validate the source: if source.type === 'connector_tool' require source.connector to be present, else mark the artifact non-refreshable.","Re-run the original connector tool to regenerate the artifact with full source metadata.","Add a backfill migration that reconstructs connector metadata from provenance/history for existing artifacts."],"exampleFix":"// before\nif (source.type === 'connector_tool') {\n  const connector = source.connector;\n  if (connector === undefined) throw new Error('connector refresh source requires connector metadata');\n}\n\n// after\nif (source.type === 'connector_tool' && !source.connector) {\n  return { ok: false, reason: 'source_missing_connector_metadata' };\n}\nif (source.type === 'connector_tool') {\n  const connector = source.connector!;\n  // ... proceed\n}","handlingStrategy":"type-guard","validationCode":"import type { LiveArtifactSource } from '@open-design/contracts';\n\nfunction isRefreshableConnectorSource(source: LiveArtifactSource): boolean {\n  return source.type !== 'connector_tool' || source.connector !== undefined;\n}\n\nif (!isRefreshableConnectorSource(source)) {\n  return { ok: false, reason: 'source_missing_connector_metadata' };\n}","typeGuard":"import type { LiveArtifactSource } from '@open-design/contracts';\n\nfunction hasConnectorMetadata(source: LiveArtifactSource): source is LiveArtifactSource & { connector: { connectorId: string; toolName: string } } {\n  return source.type === 'connector_tool'\n    && source.connector !== undefined\n    && typeof source.connector.connectorId === 'string'\n    && typeof source.connector.toolName === 'string';\n}","tryCatchPattern":"try {\n  return await executeRefreshSource({ projectsRoot, projectId, source, signal });\n} catch (err) {\n  if (err instanceof Error && err.message === 'connector refresh source requires connector metadata') {\n    return { ok: false, reason: 'source_missing_connector_metadata' };\n  }\n  throw err;\n}","preventionTips":["Validate connector_tool sources carry connector metadata before scheduling a refresh.","Backfill connector metadata for existing artifacts after schema/provider changes.","Re-run the originating connector tool to regenerate a source with full metadata."],"tags":["validation","live-artifacts","data-integrity","connector"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}