Skip to content

Project.generate() fails on second+ screens: projection assumes first-screen shape #315

@threepixel-dev

Description

@threepixel-dev

Summary

Project.generate() works for the first screen in a project and then consistently throws StitchError: Incomplete API response from generate_screen_from_text: expected object at projection path on every subsequent screen in the same project. The underlying generate_screen_from_text MCP call succeeds and returns a complete screen; the SDK's hardcoded projection just lands in the wrong place.

Reproduce

SDK version: @google/stitch-sdk@0.1.0 (Node 22, Linux x64).

import { stitch } from "@google/stitch-sdk";

const project = await stitch.createProject("multi-screen-repro");
await project.generate("Home page");          // ✅ OK
await project.generate("Treatments page");    // ❌ throws StitchError

Root cause

packages/sdk/generated/src/project.ts hardcodes:

const _projected = raw?.outputComponents?.[1]?.design?.screens?.[0];

That index is correct for the first screen but not for subsequent screens in the same project. On call 1, Stitch returns a 6-component response that includes a designSystem prefix block at index 0, pushing the screen to index 1. On call 2+, the designSystem block is omitted (already established for the project), the response is one component shorter, and the screen sits at index 0.

Raw shape diff

Call 1 (fresh project, first screen) — 6 components:

outputComponents = [
  { designSystem: ... },      // index 0
  { design: { screens: [...] } },   // index 1 — SDK finds screen here
  { text: ... },
  { suggestion: ... },
  { suggestion: ... },
  { suggestion: ... }
]

Call 2 (same project, second screen) — 5 components:

outputComponents = [
  { design: { screens: [...] } },   // index 0 — SDK looks at index 1 and finds text instead
  { text: ... },
  { suggestion: ... },
  { suggestion: ... },
  { suggestion: ... }
]

Both responses contain a complete screen at <some index>.design.screens[0]; only the index varies.

Suggested fix

Scan outputComponents for the first entry whose .design.screens[0] is present, instead of hardcoding index 1:

let _projected;
for (const component of raw?.outputComponents ?? []) {
  const screen0 = component?.design?.screens?.[0];
  if (screen0) { _projected = screen0; break; }
}

Backwards-compatible with both the 6-component first-screen shape and the 5-component subsequent shape. Also forward-defensive against future shape drift — any additional prefix blocks that might get added won't break the lookup.

Workaround for consumers

Call generate_screen_from_text via callTool() directly and apply the scanning projection yourself. We've shipped this workaround in production for now.

Environment

  • @google/stitch-sdk 0.1.0
  • Node.js 22
  • Linux x64 (Railway worker runtime)

Happy to provide additional repro artefacts if useful — we have the full raw JSON for both call shapes saved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions