instructure/canvas-lms · error · GraphQL::ExecutionError

No such context for #

Error message

No such context for %{context_type}#%{context_id}

What it means

Raised in SetFriendlyDescriptionMutation#get_context when context_type passes the VALID_CONTEXTS whitelist but context_type.constantize.find_by(id: context_id) returns nil. The context class is valid but no record with that id exists, so the mutation aborts with a message embedding the type and id.

Solutions

  1. Verify the context_id exists (GET /api/v1/courses/:id or /api/v1/accounts/:id) and fix the id passed to the mutation
  2. Ensure contextId is serialized as the numeric id, not 'undefined'/'null'/asset-string forms like 'course_42'
  3. Check the request targets the correct shard/root account containing that course or account
  4. If the record was deleted, restore it or use a different context for the friendly description

Example fix

// before: unparsed asset string id
const [type, rawId] = contextAssetString.split('_');
setFriendlyDescription(input: { contextType: type.capitalize, contextId: rawId, ... })

// after: verify the record exists first
const ctx = await canvas.get(`/api/v1/courses/${rawId}`);
if (!ctx?.id) throw new Error(`context not found: ${type}_${rawId}`);
setFriendlyDescription(input: { contextType: 'Course', contextId: ctx.id, ... });
Defensive patterns

Strategy: validation

Validate before calling

async function requireContext(contextType, contextId) {
  const id = parseInt(contextId, 10);
  if (!Number.isInteger(id) || id <= 0) throw new Error(`Invalid context id: ${contextId}`);
  const base = contextType === 'Account' ? '/api/v1/accounts' : '/api/v1/courses';
  const res = await fetch(`${base}/${id}`);
  if (!res.ok) throw new Error(`No such context for ${contextType}#${id}`);
  return res.json();
}

Type guard

const isRealContextId = (v) =>
  (typeof v === 'number' && Number.isInteger(v) && v > 0) ||
  (typeof v === 'string' && /^\d+$/.test(v));

Try / catch

try {
  await setFriendlyDescription({ variables: { contextType, contextId, outcomeId, description } });
} catch (e) {
  if (e.message.startsWith('No such context for')) {
    // parse contextType/contextId from the message for user feedback
    invalidateContextCache();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling setFriendlyDescription with a valid contextType ('Course' or 'Account') but a context_id that does not exist, was deleted, is on another shard, or is non-numeric/garbage that find_by cannot match.

Common situations: Stale course/account ids after deletion or environment switch (test vs prod); unparsed input where contextId is nil or a string like 'undefined'; cross-shard id mistakes in Canvas's Switchman setup; truncated or mistyped ids in API scripts.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/a4d8b07605730925. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/set_friendly_description.rb:93

    unless context.available_outcome(outcome.id, allow_global: true)
      raise GraphQL::ExecutionError, I18n.t(
        "Outcome %{outcome_id} is not available in context %{context_type}#%{context_id}",
        outcome_id: outcome.id.to_s,
        context_id: context.id.to_s,
        context_type: context.class.name
      )
    end
  end

  def get_context(context_type, context_id)
    unless VALID_CONTEXTS.include?(context_type)
      raise GraphQL::ExecutionError, I18n.t("Invalid context type")
    end

    context_type.constantize.find_by(id: context_id).tap do |context|
      unless context
        raise GraphQL::ExecutionError, I18n.t(
          "No such context for %{context_type}#%{context_id}",
          context_type:,
          context_id: context_id.to_s
        )
      end
    end
  end

  def get_outcome(outcome_id)
    LearningOutcome.active.find_by(id: outcome_id).tap do |outcome|
      unless outcome
        raise GraphQL::ExecutionError, I18n.t(
          "No such outcome for id %{id}", { id: outcome_id }
        )
      end
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)