Skip to content

fix: guard against null ITextEditor document provider - #585

Draft
dungdong-aws wants to merge 1 commit into
Amazon-Q-Developer:mainfrom
dungdong-aws:fix/eclipse-null-document-provider-guard
Draft

dungdong-aws wants to merge 1 commit into
Amazon-Q-Developer:mainfrom
dungdong-aws:fix/eclipse-null-document-provider-guard

Conversation

@dungdong-aws

@dungdong-aws dungdong-aws commented Oct 14, 2026 •

Copy link
Copy Markdown

Problem

Fixes #510. An editor that implements ITextEditor but returns null from
getDocumentProvider() makes Amazon Q raise

Cannot invoke "IDocumentProvider.getDocument(Object)" because the return value of
"ITextEditor.getDocumentProvider()" is null

The reporter sees the workbench's generic "An error has occurred. See error log for
more details." dialog simply by activating such an editor — no Amazon Q action is
required.

Root cause

IDocumentProvider is an optional part of the Eclipse text-editor API;
ITextEditor.getDocumentProvider() is allowed to return null, and third party
editors do. AutoTriggerPartListener.partActivated dereferences it unconditionally
for every activated ITextEditor:

var document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
document.addDocumentListener(docListener);

Because partActivated is an IPartListener2 callback, the NPE escapes into the
workbench, which reports it through its status handler — hence the dialog on every
activation. onStart() in the same class already null-checked the document but
not the provider.

The repository already treats this API as nullable elsewhere — AbapUtil (~221),
AmazonQLspClientImpl (~505) and InlineChatSession (~109) all guard it — so this
change applies the existing in-repo convention to the paths that still miss it.

Changes

Production (~50 lines):

  • AutoTriggerPartListener: added a private resolveDocument(ITextEditor) helper
    that returns null when the editor, its provider, or its document is null.
    partActivated now returns early instead of attaching a listener, and onStart
    reuses the helper so a null provider re-arms the existing 1-second poll exactly
    as a null document already did.
  • QEclipseEditorUtils: added an equivalent private getDocument(ITextEditor)
    helper and routed getSelectionRange and insertAtCursor through it; both now
    return Optional.empty() rather than throwing. These are the send-to-chat and
    insert-at-cursor paths, reachable with the same editor.

Behaviour for editors that do have a document provider is unchanged.

The guards are deliberately local to each class rather than shared through a public
QEclipseEditorUtils helper: QEclipseEditorUtils is replaced by mockStatic in
four existing test classes, so routing AutoTriggerPartListener through it would
make the guard unobservable in that class's own unit tests.

Remaining call sites (intentionally not in this PR)

The same unchecked dereference exists in the inline-chat and agentic-edit paths —
InlineChatDiffManager (99, 100, 198, 199, 244), InlineChatUIManager (425) and
AmazonQLspClientImpl (398, 400). Those are not part of this fix because they sit
behind InlineChatSession.startSession, which already wraps the call in
try/catch and ends the session, so they produce a handled failure rather than the
reported workbench dialog. Keeping this PR to the unhandled path keeps it atomic
and reviewable; the inline-chat sites are worth a separate follow-up.

Edge cases covered

  • getDocumentProvider() returns null → activation ignored, no listener attached,
    no active document recorded, and the subsequent partDeactivated is also a no-op.
  • Provider present but getDocument(input) returns null → same no-op.
  • onStart with a null provider → retries on the existing timer instead of throwing
    on the UI thread.
  • Editor itself null (already possible via getActiveTextEditor()) → handled by
    the same helper.

Tests / validation

  • Added AutoTriggerPartListenerTest#testPartActivateWithNullDocumentProvider and
    #testPartActivateWithNullDocument, both asserting no throw and that no document
    listener is attached or detached.
  • Not run locally. This environment has no Maven/Tycho toolchain and cannot
    enforce a memory cgroup cap, so no build or test execution was performed. CI on
    this PR (mvn -B package, which also runs checkstyle with
    includeTestSourceDirectory) is the first full validation.
  • Not reproduced at runtime. No Eclipse instance was run and no custom
    provider-less ITextEditor was built. The mechanism is established from source
    and from the exact exception message on the issue; the throw site is inferred from
    the activation-only reproduction, since the issue quotes the message without a
    stack trace.

Release target

Base main (repository default; the Maven workflow runs for PRs targeting main).

Ticket

ITextEditor.getDocumentProvider() is optional in the Eclipse API and may
return null for third party editors. AutoTriggerPartListener dereferenced
it unconditionally on every editor activation, so the NPE escaped the
IPartListener2 callback and the workbench showed a generic error dialog.

Resolve the provider and document defensively in AutoTriggerPartListener
(partActivated and onStart) and in QEclipseEditorUtils.getSelectionRange
and insertAtCursor, degrading to a no-op instead of throwing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When an editor in Eclipse is an ITextEditor instance without DocumentProvider, an error message is thrown.

1 participant