{"record":{"id":"aeaad39ec2cdee43","repo":"paperclipai/paperclip","slug":"anthropic-did-not-return-usable-agent-and-environm","errorCode":null,"errorMessage":"Anthropic did not return usable Agent and Environment identities","messagePattern":"Anthropic did not return usable Agent and Environment identities","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/src/commands/managed-agent.ts","lineNumber":339,"sourceCode":"  const validated = validateManagedAgentSetup(options);\n  const normalizedOptions: ManagedAgentSetupOptions = {\n    ...options,\n    profileKey: validated.profileKey,\n    displayName: validated.displayName,\n    apiKeySecretId: validated.apiKeySecretId,\n    model: validated.model,\n    agentId: validated.agentId,\n    agentVersion: validated.agentVersion,\n    environmentId: validated.environmentId,\n  };\n  const [environment, agent] = await Promise.all([\n    resolveEnvironment(validated.anthropicApiKey, normalizedOptions),\n    resolveAgent(validated.anthropicApiKey, normalizedOptions),\n  ]);\n  const agentId = String(agent.id ?? \"\");\n  const environmentId = String(environment.id ?? \"\");\n  if (!agentId || !environmentId) {\n    throw new Error(\"Anthropic did not return usable Agent and Environment identities\");\n  }\n\n  const versions = await listAll(\n    validated.anthropicApiKey,\n    `/v1/agents/${encodeURIComponent(agentId)}/versions`,\n  );\n  const version = normalizedOptions.agentVersion\n    ?? String(agent.version ?? versions.at(-1)?.version ?? \"\");\n  const pinnedAgent = version\n    ? versions.find((entry) => String(entry.version) === version)\n    : undefined;\n  if (!version || !pinnedAgent) {\n    throw new Error(\"Anthropic did not return a usable pinned Agent version\");\n  }\n  if (String(pinnedAgent.id ?? \"\") !== agentId) {\n    throw new Error(\"Anthropic pinned Agent version identity does not match the selected Agent\");\n  }\n  assertSafeManagedAgent(pinnedAgent);","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/cli/src/commands/managed-agent.ts#L321-L357","documentation":"setupManagedAgent resolves the Agent and Environment in parallel and coerces both `id` fields to strings; if either is empty the Anthropic API returned a resource without a usable identity. Throwing here prevents writing credentials/config based on malformed responses rather than failing later with obscure 404s.","triggerScenarios":"POST/GET responses from /v1/environments or /v1/agents missing an `id` field — e.g. API contract change, an error-shaped body that still parsed as an object, or a proxy/gateway returning unexpected JSON.","commonSituations":"Anthropic API version drift where ids moved to a different field; a corporate proxy stripping or reshaping response bodies; using a stubbed/mock endpoint that omits ids; transient partial responses.","solutions":["Log the raw JSON responses of /v1/environments and /v1/agents to see which resource lacks `id`.","Check the Anthropic API version / changelog for identity-field changes and pin a supported api-version header.","Retry after ruling out transient gateway interference (direct curl to api.anthropic.com).","Update the Paperclip CLI to the latest version if Anthropic changed the response shape."],"exampleFix":"// before: blindly trusting the API shape\nconst agent = await anthropicRequest(key, \"POST\", \"/v1/agents\", body);\n// after: like the library, validate before use\nconst agentId = String(agent.id ?? \"\");\nif (!agentId) throw new Error(\"Anthropic did not return a usable Agent id\");","handlingStrategy":"type-guard","validationCode":"// validate the API response shape before consuming it\nfunction hasUsableId(r: unknown): r is { id: string | { id: string } } {\n  return typeof r === \"object\" && r !== null && (typeof (r as any).id === \"string\" || typeof (r as any).id?.id === \"string\");\n}\nconst agent = await anthropicRequest(key, \"POST\", \"/v1/agents\", body);\nif (!hasUsableId(agent)) throw new Error(\"Anthropic agent response missing id\");","typeGuard":"function hasStringId(v: unknown): v is { id: string } {\n  return typeof v === \"object\" && v !== null && typeof (v as { id?: unknown }).id === \"string\" && (v as { id: string }).id.length > 0;\n}","tryCatchPattern":"try {\n  await setupManagedAgent(options);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"usable Agent and Environment identities\")) {\n    // capture raw responses for diagnosis, then retry or report API drift\n    const raw = await anthropicRequest(key, \"GET\", \"/v1/agents\");\n    console.error(\"Unexpected API shape:\", JSON.stringify(raw).slice(0, 500));\n  }\n  throw e;\n}","preventionTips":["Pin the Anthropic api-version header and review changelogs on upgrades.","Don't route API calls through reshaping proxies/gateways.","Keep the CLI updated when Anthropic changes response schemas.","Log raw failing responses (redacted) before retrying."],"tags":["anthropic","api-response","missing-id","contract-change"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-09-02T18:44:00.616Z","contentChangedAt":"2026-09-02T18:44:00.616Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}