chenhg5/cc-connect · warning

codex app-server: no pending approval for request %s

Error message

codex app-server: no pending approval for request %s

What it means

RespondPermission looks up a pending approval channel by requestID in s.pendingApprovals; if no entry exists it returns this error. It means a permission decision arrived for a request the session is not currently waiting on — the request already resolved, expired, or the ID is wrong.

Source

Thrown at agent/codex/appserver_session.go:547

		if err := os.WriteFile(fpath, img.Data, 0o644); err != nil {
			return "", nil, fmt.Errorf("codex app-server: save image: %w", err)
		}
		imagePaths = append(imagePaths, fpath)
	}

	if strings.TrimSpace(prompt) == "" {
		prompt = "Please analyze the attached image(s)."
	}

	return prompt, imagePaths, nil
}

func (s *appServerSession) RespondPermission(requestID string, result core.PermissionResult) error {
	s.approvalsMu.Lock()
	ch := s.pendingApprovals[requestID]
	s.approvalsMu.Unlock()
	if ch == nil {
		return fmt.Errorf("codex app-server: no pending approval for request %s", requestID)
	}
	select {
	case ch <- result:
	default:
	}
	return nil
}

func (s *appServerSession) handleServerRequest(probe map[string]json.RawMessage) {
	rawID := probe["id"]
	var method string
	if err := json.Unmarshal(probe["method"], &method); err != nil {
		return
	}
	params := probe["params"]

	switch method {
	case "item/commandExecution/requestApproval", "item/fileChange/requestApproval":

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use the exact requestID delivered in the approval request event
  2. Treat this as benign when it's a duplicate click — check-and-ignore rather than surfacing an error to the user
  3. Handle approvals within the timeout window before they are cleaned up
  4. If it happens after restarts, persist/re-emit pending approval requests or invalidate stale buttons

Example fix

// before
err := sess.RespondPermission(requestID, core.PermissionResult{Approved: true})
if err != nil { return err } // double-click now fails the handler
// after
if err := sess.RespondPermission(requestID, res); err != nil && !alreadyHandled(requestID) {
    slog.Warn("approval already resolved", "requestID", requestID)
}
Defensive patterns

Strategy: type-guard

Validate before calling

func hasPendingApproval(sess *appServerSession, requestID string) bool {
    sess.approvalsMu.Lock(); defer sess.approvalsMu.Unlock()
    return sess.pendingApprovals[requestID] != nil
}

Type guard

if hasPendingApproval(sess, requestID) {
    _ = sess.RespondPermission(requestID, res)
} else {
    slog.Warn("approval already resolved or unknown", "requestID", requestID)
}

Try / catch

if err := sess.RespondPermission(requestID, res); err != nil {
    slog.Warn("respond permission: %v (likely duplicate/late reply)", err) // do not fail the user flow
}

Prevention

When it happens

Trigger: Calling RespondPermission with a requestID that was never registered, or after the approval already completed, or after the session restarted and its in-memory pendingApprovals map was cleared.

Common situations: User double-clicks an approval button in the messaging platform so the second RespondPermission finds nothing; approval timed out and pendingApprovals was cleaned up; process restart between the request and the user's reply; passing the turn id instead of the approval request id.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/9d880cc80f5dba2f. Report an issue: GitHub.