Skip to content

Three CloudWatch Logs read operations answer an empty 200 where their pages publish ResourceNotFoundException at 400 #1224

Description

@scttfrdmn

Three of CloudWatch Logs' four paginating operations publish ResourceNotFoundException for a log
group that does not exist. Substrate answers HTTP 200 with an empty listing at all three, so a
caller cannot tell "this group is empty" from "this group was never created" — and a consumer whose
error path exists to handle a typo'd or not-yet-created log group never reaches it.

What the pages publish

API_DescribeLogStreams, API_GetLogEvents and API_FilterLogEvents each publish, in their own
Errors sections:

ResourceNotFoundException
The specified resource does not exist.
HTTP Status Code: 400

The status is 400, not 404, and that is the page's own figure — it reads like a typo and is not.
docs/services.md's CloudWatch Logs section already records this for PutRetentionPolicy and notes
that "Substrate's older group- and stream-level not-found responses on the other operations still use
404 and are not changed here"
, so the plugin is inconsistent with itself as well as with the pages.

API_DescribeLogGroups publishes no ResourceNotFoundException — its Errors section is
InvalidParameterException/400 and ServiceUnavailableException/500 only — which is right, because a
listing with a prefix that matches nothing is legitimately empty. So this is a three-site issue, not
four.

What substrate does

  • describeLogStreams checks that logGroupName is present, then reads the stream-names index
    directly. A group that does not exist has no index, so loadStringIndex returns nil, nil and the
    operation answers {"logStreams":[]}.
  • getLogEvents checks both required members, then reads the events key. No group record and no stream
    record is consulted, so a nonexistent group or a nonexistent stream answers {"events":[]}.
  • filterLogEvents is the same shape via the stream index.

putLogEvents does resolve both, and answers ResourceNotFoundException at 404 — so the code
exists in the plugin, at the wrong status, and is simply not reached from the three read paths.

Why it matters beyond the missing code

Found while converting these four operations' pagination tokens for #1086 (see
emulator/cloudwatchlogs_pagination.go, which records this as out of that change's scope). The
ordering interacts: #1086 established that the token is decoded before any state is read, and
GetLogEvents also requires logStreamName. Adding a resource resolution puts a third refusal in the
sequence, and AWS publishes nothing about which of a bad token and a missing resource wins. The
in-tree precedent is SNS ListSubscriptionsByTopic (#926), where the NotFound keeps precedence
because the topic is the resource the request addresses and the token is a continuation of a listing
over it — the same argument applies to a log group, and the seal in
pagination_token_refusal_test.go is already written to accommodate it (sealGetKey).

Acceptance criteria

  • DescribeLogStreams, GetLogEvents and FilterLogEvents answer ResourceNotFoundException at
    HTTP 400 for a logGroupName that has no record, with a message naming the group.
  • GetLogEvents answers the same for a logStreamName that has no record under an existing group,
    matching putLogEvents' existing distinction between the two.
  • DescribeLogGroups is unchanged — a prefix matching nothing stays an empty 200, because its
    page publishes no such code.
  • The existing 404s elsewhere in the plugin are either moved to 400 in the same change or the split
    is recorded with the reason; the current docs/services.md sentence saying they "are not changed
    here" must stop being the whole of the explanation.
  • Precedence against Fifteen offset-token sites still answer page one for a token substrate never issued #1086's token refusal is decided, argued from SNS ListSubscriptionsByTopic,
    recorded in the plugin, and asserted — including that the token is still refused before any state
    is read when the resource does exist.
  • Tests build every fixture through real wire calls per cfn: the aws:cloudformation:* stamp covers EC2 only, so the rest of a stack is unstamped #765, and assert the code and the status,
    since the status is the part that is wrong twice over.
  • docs/services.md drops this from the list of divergences Fifteen offset-token sites still answer page one for a token substrate never issued #1086 left in place.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions