Problem
In the studio flow builder, "+ From Playlist" opens a picker whose cards are all blank — no images render.
Cause
It isn't a broken URL. The modal lists Keyframe entities, and their image column is null.
GET /v1/playlist/:uuid/keyframes on staging, two playlists, two different users:
{"uuid":"833d819a-db68-4f81-a16a-1f8ea32fb9db","name":"kf_FLUX.1 [schnell] 23","image":null,"dreams":[]}
{"uuid":"75954d77-43b1-40ac-8c36-ab246062b887","name":"kf_FLUX.1 [schnell] 7 → …","image":null,"dreams":[]}
add-keyframes-from-playlist-modal.tsx renders src={item.keyframe!.image}, so <img src={null}> requests nothing and draws an empty box.
The null originates at creation. flow-keyframes.ts → ensureFlowKeyframe() posts only a name:
axiosClient.post("/v1/keyframe", { name: `kf_${name}` })
createKeyframeSchema accepts nothing but name; giving a keyframe an image requires a separate multipart upload (POST /v1/keyframe/:uuid/image/init → /image/complete), which Studio never performs. So every Studio-created keyframe has image: null for its whole life.
This is not a signing problem — keyframe.image and dream.thumbnail both go through the same signKey(). In the very same responses, playlist.thumbnail and user.avatar come back as valid signed URLs.
Fix
The modal shouldn't be using keyframes at all. It should show dreams from the selected playlist — effectively "+ My Images" filtered to one playlist.
GET /v1/playlist/:uuid/items already returns full Dream rows (leftJoinAndSelect), including populated thumbnail and processedMediaWidth/Height. The Images tab's add-from-playlist-modal already does exactly this and works.
Suggested direction:
- Point the flow builder's "+ From Playlist" at
/v1/playlist/:uuid/items, filtered to mediaType: image.
- Reuse the picker UI from
select-image-dream-modal so both surfaces behave identically.
- Keyframe records can still be created on save (
ensureFlowKeyframe) — they just stop being the browse/display source.
Also affected
ItemCard's getThumbnail returns keyframe.image (item-card.tsx:147), so the playlist page's Keyframes tab is blank for these same keyframes. Worth deciding whether that surface should keep showing keyframes at all.
Note
The flow strip itself looks fine because it draws imageUrl from the flow store — the source dream's image — which never touches the keyframe row.
Found while fixing #706 (see #715); the two are unrelated causes.
🤖 Generated with Claude Code
Problem
In the studio flow builder, "+ From Playlist" opens a picker whose cards are all blank — no images render.
Cause
It isn't a broken URL. The modal lists
Keyframeentities, and theirimagecolumn isnull.GET /v1/playlist/:uuid/keyframeson staging, two playlists, two different users:{"uuid":"833d819a-db68-4f81-a16a-1f8ea32fb9db","name":"kf_FLUX.1 [schnell] 23","image":null,"dreams":[]} {"uuid":"75954d77-43b1-40ac-8c36-ab246062b887","name":"kf_FLUX.1 [schnell] 7 → …","image":null,"dreams":[]}add-keyframes-from-playlist-modal.tsxrenderssrc={item.keyframe!.image}, so<img src={null}>requests nothing and draws an empty box.The
nulloriginates at creation.flow-keyframes.ts→ensureFlowKeyframe()posts only a name:createKeyframeSchemaaccepts nothing butname; giving a keyframe an image requires a separate multipart upload (POST /v1/keyframe/:uuid/image/init→/image/complete), which Studio never performs. So every Studio-created keyframe hasimage: nullfor its whole life.This is not a signing problem —
keyframe.imageanddream.thumbnailboth go through the samesignKey(). In the very same responses,playlist.thumbnailanduser.avatarcome back as valid signed URLs.Fix
The modal shouldn't be using keyframes at all. It should show dreams from the selected playlist — effectively "+ My Images" filtered to one playlist.
GET /v1/playlist/:uuid/itemsalready returns fullDreamrows (leftJoinAndSelect), including populatedthumbnailandprocessedMediaWidth/Height. The Images tab'sadd-from-playlist-modalalready does exactly this and works.Suggested direction:
/v1/playlist/:uuid/items, filtered tomediaType: image.select-image-dream-modalso both surfaces behave identically.ensureFlowKeyframe) — they just stop being the browse/display source.Also affected
ItemCard'sgetThumbnailreturnskeyframe.image(item-card.tsx:147), so the playlist page's Keyframes tab is blank for these same keyframes. Worth deciding whether that surface should keep showing keyframes at all.Note
The flow strip itself looks fine because it draws
imageUrlfrom the flow store — the source dream's image — which never touches the keyframe row.Found while fixing #706 (see #715); the two are unrelated causes.
🤖 Generated with Claude Code