/goal drops attached/mentioned files from the goal objective

Open 💬 0 comments Opened Jun 9, 2026 by douglasmonsky

What version of the Codex App are you using (From “About Codex” dialog)?

1.2026.119

What subscription do you have?

Pro 20x

What platform is your computer?

Darwin 25.5.0 arm64 arm

What issue are you seeing?

When using /goal with an attached or mentioned file, the file does not appear to be sent through with the goal. I noticed this with a .txt file, but the code path suggests this is probably not text-file-specific. It looks like /goal currently reduces the input to a plain objective string and discards the structured input payload that normally carries file mentions and attachments.

What steps can reproduce the bug?

  1. Start Codex in a saved session where /goal is available.
  2. Attach or mention a file in the composer, for example a .txt file.
  3. Submit a goal that references it, for example:

/goal summarize and track work from @notes.txt

What is the expected behavior?

The file should be included with the goal objective, or at least resolved into the objective context, so the goal can operate on the attached/mentioned file.

If /goal intentionally does not support file attachments or file mentions, Codex should reject the submission with a clear message instead of silently dropping the file.

Additional information

Suspected cause

The /goal inline slash-command path appears to discard structured input data and only forwards a plain String.

In chatwidget/slash_dispatch.rs, inline slash-command handling has a richer payload type:

struct PreparedSlashCommandArgs {
    args: String,
    text_elements: Vec<TextElement>,
    local_images: Vec<LocalImageAttachment>,
    remote_image_urls: Vec<String>,
    mention_bindings: Vec<MentionBinding>,
    source: SlashCommandDispatchSource,
}

Other inline commands that need the full submitted payload use a helper that preserves structured input. For example, /plan and /side route through prepared_inline_user_message(...), which can carry text_elements, image attachments, remote image URLs, and mention bindings.

The /goal path does not appear to use that helper. It trims the inline args and sends only the objective string:

SlashCommand::Goal => {
    if !self.config.features.enabled(Feature::Goals) {
        return;
    }
    let objective = args.trim();
    if objective.is_empty() {
        self.add_info_message(GOAL_USAGE.to_string(), Some(GOAL_USAGE_HINT.to_string()));
        return;
    }
    if let Some(thread_id) = self.thread_id {
        self.app_event_tx.send(AppEvent::SetThreadGoalObjective {
            thread_id,
            objective: objective.to_string(),
            mode: ThreadGoalSetMode::ConfirmIfExists,
        });
    } else {
        self.queue_user_message(UserMessage {
            text: format!("/goal {args}"),
            local_images: Vec::new(),
            remote_image_urls: Vec::new(),
            text_elements: Vec::new(),
            mention_bindings: Vec::new(),
        });
    }
}

That looks like the direct cause. Any file/attachment state stored in text_elements, local_images, remote_image_urls, or mention_bindings is dropped.

The event type also only carries a plain string:

AppEvent::SetThreadGoalObjective {
    thread_id: ThreadId,
    objective: String,
    mode: ThreadGoalSetMode,
}

The app-level handler then forwards that string to set_thread_goal_objective(...), which ultimately calls:

app_server
    .thread_goal_set(thread_id, Some(objective), Some(status), token_budget)
    .await;

So by the time the goal reaches the app-server request layer, there is no remaining structured user input available to resolve or attach the file.

There is also a second loss path before the thread exists: when /goal ... is queued, the code constructs a UserMessage with empty local_images, remote_image_urls, text_elements, and mention_bindings, which also drops any file payload.

Possible fixes

Preserve structured input for /goal

Change the goal-setting path so it can carry structured input instead of only objective: String.

For example:

AppEvent::SetThreadGoalObjective {
    thread_id: ThreadId,
    objective: UserMessage,
    mode: ThreadGoalSetMode,
}

or introduce a goal-specific payload type containing:

struct GoalObjectiveInput {
    text: String,
    text_elements: Vec<TextElement>,
    mention_bindings: Vec<MentionBinding>,
    local_images: Vec<LocalImageAttachment>,
    remote_image_urls: Vec<String>,
}

Then /goal could use the same structured-input preparation path as other inline commands instead of calling objective.to_string().

The app-server thread/goal/set API may also need to accept structured input, or the TUI/app layer should resolve file mentions into objective/context before sending the goal request.

View original on GitHub ↗