Skip to content

bug: URL-based chat seeding (send-on-load / submit-on-load) ignores agentId, ignores user-prompt, and misparses booleans #13727

Description

@blambert

Summary

Deep-linking into the chat app with query params to pre-seed and auto-send a prompt is broken in three distinct ways. All three live in web/src/views/AppPage.tsx, web/src/hooks/useChatSessionController.ts, and web/src/app/app/services/searchParams.ts.

Environment

  • Deployment: self-hosted 4.4.7
  • Branch/commit: main
  • Browser: Chrome Stable

Reproduction

https://onyx.example.org/app?agentId=8&user-prompt=Example%20prompt&send-on-load=true
https://onyx.example.org/app?agentId=8&user-prompt=Example%20prompt&submit-on-load=true
https://onyx.example.org/app?agentId=8&user-prompt=Example%20prompt&send-on-load=false

Expected vs. Actual

# Scenario Expected Actual
1 send-on-load=true + agentId=8 Prompt sent as agent 8 Sent as default persona
2 submit-on-load=true + agentId=8 Prompt sent as agent 8, prompt visible in user bubble Sent as default persona and user bubble is empty
3 send-on-load=false Nothing auto-sends Prompt auto-sends anyway

Root causes

3 — send-on-load is truthiness-checked, not parsed

// web/src/views/AppPage.tsx
useEffect(() => {
  if (searchParams?.get(SEARCH_PARAM_NAMES.SEND_ON_LOAD)) {
    processSearchParamsAndSubmitMessage(searchParams.toString());
  }
}, [searchParams, router]);

searchParams.get() returns the string "false", which is truthy [1]. A correct parser already exists in the codebase and simply isn't used here:

// web/src/app/app/services/searchParams.ts
export function shouldSubmitOnLoad(searchParams) {
  const raw = searchParams?.get(SEARCH_PARAM_NAMES.SUBMIT_ON_LOAD);
  return raw === "true" || raw === "1";
}

[2] — note it only covers SUBMIT_ON_LOAD. SEND_ON_LOAD has no equivalent.

2 — submit-on-load submits firstMessage, not user-prompt

// web/src/app/app/page.tsx
const firstMessage = searchParams.firstMessage;
return <AppPage firstMessage={firstMessage} />;

// web/src/hooks/useChatSessionController.ts — initialSessionFetch()
setSelectedAgentFromId(null);                       // ← agent reset to default
...
if (shouldSubmitOnLoad(searchParams) && !submitOnLoadPerformed.current) {
  submitOnLoadPerformed.current = true;
  await onSubmit({
    message: firstMessage || "",                    // ← user-prompt never read
    currentMessageFiles: [],
    deepResearch: false,
  });
}

[3]

Two bugs in one block:

  • The message comes from a different, undocumented param (firstMessage), so user-prompt is dropped and an empty string is submitted → blank user bubble.
  • setSelectedAgentFromId(null) runs immediately before, actively clearing any agent selection before the send.

1 — send-on-load never reads agentId

processSearchParamsAndSubmitMessage reads user-prompt, builds filters, deletes SEND_ON_LOAD, and calls onSubmit — it never touches agentId [1]. Agent resolution is a separate async path (useAgentControlleruseAgents() SWR), and useChatController creates the session with liveAgent?.id || 0. On a cold load the effect fires while agents is still loading, so the session is bound to persona 0 before agentId=8 ever resolves. The effect's dependency array is [searchParams, router] — no gate on isLoadingAgents [1].


Suggested fixes

Consider consolidating send-on-load and submit-on-load. Two params doing nearly the same thing through two different code paths is very likely how this divergence happened — deprecate one, alias it to the other.

  1. Gate auto-submit on agent readiness. Don't fire until !isLoadingAgents, and ideally until liveAgent?.id === Number(searchParams.get("agentId")).
  2. Pass agentId explicitly through the submit path rather than relying on liveAgent having resolved — e.g. const personaId = liveAgent?.id ?? agentIdFromParams ?? DEFAULT_AGENT_ID.
  3. Don't call setSelectedAgentFromId(null) on the submit-on-load path when agentId is present in the URL.
  4. Read user-prompt in the submit-on-load path, or at minimum fall back: firstMessage || searchParams.get(SEARCH_PARAM_NAMES.USER_PROMPT).
  5. Use shouldSubmitOnLoad-style parsing for SEND_ON_LOAD (=== "true" || === "1").

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