{"record":{"id":"a5c7656996d9ba1b","repo":"Hmbown/CodeWhale","slug":"choose-both-a-provider-and-a-model","errorCode":null,"errorMessage":"Choose both a provider and a model.","messagePattern":"Choose both a provider and a model\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":576,"sourceCode":"\nexport function modelOptionLabel(model) {\n  const id = String(model?.id || \"\").trim();\n  return model?.image_input === \"supported\" ? `${id} · Vision` : id;\n}\n\nexport function providerOptionLabel(provider) {\n  const id = String(provider?.id || \"\").trim();\n  const displayName = String(provider?.display_name || id).trim();\n  const exactId = String(provider?.model_provider_id || \"\").trim();\n  return exactId && exactId !== id ? `${displayName} · ${exactId}` : displayName;\n}\n\nexport function buildCreateThreadRequest(providerId, model, modelProviderId = \"\") {\n  const modelProvider = String(providerId || \"\").trim();\n  const exactProviderId = String(modelProviderId || \"\").trim();\n  const selectedModel = String(model || \"\").trim();\n  if (!modelProvider || !selectedModel) {\n    throw new Error(\"Choose both a provider and a model.\");\n  }\n  const request = { model_provider: modelProvider, model: selectedModel };\n  if (exactProviderId) request.model_provider_id = exactProviderId;\n  return request;\n}\n\nexport function threadProviderLabel(thread) {\n  const exact = String(thread?.model_provider_id || \"\").trim();\n  const generic = String(thread?.model_provider || \"\").trim();\n  return exact || generic;\n}\n\nexport function claimInFlight(inFlight, key) {\n  const action = String(key || \"\").trim();\n  if (!(inFlight instanceof Set) || !action || inFlight.has(action)) return false;\n  inFlight.add(action);\n  return true;\n}","sourceCodeStart":558,"sourceCodeEnd":594,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_web/app.mjs#L558-L594","documentation":"buildCreateThreadRequest validates that both a provider id and a model name are present before constructing the create-thread request body. An empty/whitespace providerId or model means the Runtime could not select a model, so it throws.","triggerScenarios":"Calling buildCreateThreadRequest('', ...) or with an empty model; provider/model selectors not yet populated (catalog load failed or still loading) when thread creation is triggered.","commonSituations":"User clicks \"new thread\" before the provider/model dropdowns finish loading; a provider was chosen but no model selected; a config default provider is missing so nothing is preselected.","solutions":["Wait for the provider/model catalog to load and ensure both selectors have values before creating a thread.","Preselect a default provider/model once the catalog arrives.","Check why the model list is empty for the chosen provider (provider down or misconfigured)."],"exampleFix":"// before\nconst req = buildCreateThreadRequest(providerSel.value, modelSel.value);\n// after\nif (!providerSel.value || !modelSel.value) { showHint('Pick a provider and model'); return; }\nconst req = buildCreateThreadRequest(providerSel.value, modelSel.value);","handlingStrategy":"validation","validationCode":"if (!providerSel.value.trim() || !modelSel.value.trim()) {\n  showHint('Choose a provider and a model');\n  return;\n}","typeGuard":"function hasModelChoice(provider, model) {\n  return String(provider || '').trim().length > 0 && String(model || '').trim().length > 0;\n}","tryCatchPattern":"try {\n  const req = buildCreateThreadRequest(provider, model);\n} catch (e) {\n  showHint(e.message);\n  return;\n}","preventionTips":["Preselect a default provider/model once the catalog loads.","Disable the create-thread button until both selections are made.","Check that provider model catalogs load successfully on startup."],"tags":["validation","provider","model"],"backgroundTag":"missing-required-argument","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}