{"record":{"id":"f30e5cac00e98486","repo":"langgenius/dify","slug":"not-completion-app","errorCode":"not_completion_app","errorMessage":"Not Completion App","messagePattern":"Not Completion App","errorType":"console","errorClass":"NotCompletionAppError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/explore/completion.py","lineNumber":104,"sourceCode":")\nclass CompletionApi(InstalledAppResource):\n    @console_ns.expect(console_ns.models[CompletionMessageExplorePayload.__name__])\n    @console_ns.response(200, \"Success\")\n    @with_current_user\n    @with_session\n    @model_validate(CompletionMessageExplorePayload)\n    def post(\n        self,\n        req_data: CompletionMessageExplorePayload,\n        session: Session,\n        current_user: Account,\n        installed_app: InstalledApp,\n    ):\n        app_model = installed_app.app_with_session(session=session)\n        if app_model is None:\n            raise AppUnavailableError()\n        if app_model.mode != AppMode.COMPLETION:\n            raise NotCompletionAppError()\n\n        args = req_data.model_dump(exclude_none=True)\n\n        streaming = req_data.response_mode == \"streaming\"\n        args[\"auto_generate_name\"] = False\n\n        installed_app.last_used_at = naive_utc_now()\n        db.session.commit()\n\n        try:\n            response = AppGenerateService.generate(\n                session=session,\n                app_model=app_model,\n                user=current_user,\n                args=args,\n                invoke_from=InvokeFrom.EXPLORE,\n                streaming=streaming,\n            )","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/explore/completion.py#L86-L122","documentation":"Raised by CompletionApi.post when the installed app's mode is not AppMode.COMPLETION. The completion-messages endpoint exclusively serves text-generation (completion) apps; chat, agent-chat, and workflow apps must use their own endpoints. Returns HTTP 400 with error_code 'not_completion_app'. This is a route/mode contract check — the app exists but was built as a different app type than the endpoint expects.","triggerScenarios":"POST /console/installed-apps/<id>/completion-messages against an installed app whose App.mode is CHAT, AGENT_CHAT, ADVANCED_CHAT, WORKFLOW, or GENERATOR. Happens when a frontend hardcodes the completion endpoint regardless of app type, or when an app's mode was changed in Studio after installation.","commonSituations":"Frontend uses a single code path for all installed apps and hits the completion route for a chat app; user installed a chat-template app but the client assumes completion; app was converted from completion to chat after the installed_app_id was cached client-side.","solutions":["Route to /console/installed-apps/<id>/chat-messages instead if the app is a chat/agent-chat app.","Before calling, fetch the installed app's mode and branch the endpoint accordingly.","Re-open the app in Studio to confirm its current mode and update the client accordingly.","If the app should be a completion app, recreate it with the completion template."],"exampleFix":"// before: always calls completion endpoint\nconst url = `/console/installed-apps/${id}/completion-messages`\n\n// after: branch by app mode\nconst mode = installedApp.app_mode\nconst url = mode === 'completion'\n  ? `/console/installed-apps/${id}/completion-messages`\n  : `/console/installed-apps/${id}/chat-messages`","handlingStrategy":"validation","validationCode":"// Resolve the app mode from the installed-apps list, then choose the endpoint.\nfunction pickEndpoint(installedApp) {\n  return installedApp.app_mode === 'completion'\n    ? `/console/installed-apps/${installedApp.id}/completion-messages`\n    : `/console/installed-apps/${installedApp.id}/chat-messages`;\n}","typeGuard":"function isCompletionApp(entry) {\n  return entry?.app_mode === 'completion';\n}","tryCatchPattern":"try {\n  await postCompletion(id, payload);\n} catch (err) {\n  if (err.code === 'not_completion_app') {\n    // switch to the chat-messages endpoint instead of retrying\n    await postChat(id, payload);\n  } else { throw err; }\n}","preventionTips":["Always read the installed app's mode before choosing the endpoint.","Treat mode as dynamic — it can change in Studio; do not assume it on the client.","Use a single helper that branches the URL by app_mode."],"tags":["explore","app-mode","completion","routing"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}