instructure/canvas-lms · error · InvalidIDError
expected an id for #
Error message
expected an id for #{expected_type} What it means
GraphQLHelpers.parse_relay_id decodes a Relay global id via UniqueWithinType.decode and validates that the embedded type equals expected_type and the id is non-nil. Mismatches raise InvalidIDError 'expected an id for <expected_type>'.
Solutions
- Encode the correct global id with GraphQL::Schema::UniqueWithinType.encode(expected_type, id).
- Check that the id you pass matches the field's expected argument type in the schema.
- If you have a raw db id, wrap it: GraphQL::Schema::UniqueWithinType.encode('User', db_id).
Example fix
// before
user(id: "Q291cnNlLTE=") // decodes to Course-1
// after
const gid = Buffer.from('User-7').toString('base64') // 'VXNlci03'
user(id: "VXNlci03") Defensive patterns
Strategy: validation
Validate before calling
function decodeGid(gid){ const [t, id] = GraphQL::Schema::UniqueWithinType.decode(gid); return {t, id} }
if (decodeGid(gid).t !== 'Course') throw new Error('wrong gid type for this field') Type guard
function isCourseGid(gid){ try { return UniqueWithinType.decode(gid)[0] === 'Course' } catch { return false } } Try / catch
begin Helpers.parse_relay_id(gid, 'Course') rescue InvalidIDError # re-encode from raw db id or surface a client error end
Prevention
- Always take gids from prior GraphQL responses
- Never hand-decode/encode Relay ids ad hoc
- Add client-side helpers typed per entity
When it happens
Trigger: Passing a Relay id of the wrong type to a field expecting a specific type (e.g. a Course gid where a User id is expected), or passing a malformed/garbage base64 string that decodes to nil.
Common situations: Copying ids between different queries; using raw database ids instead of Relay gids; clients constructing gids manually with the wrong type name; cross-shard id formatting mistakes.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- don't know how to load #
- and cannot be used together
- and cannot be used together
- A course with that id does not exist
- A maximum of 50 assessees can be provided at once
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/bf1c6bc7341333ba.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/graphql_helpers.rb:56
parse_relay_or_legacy_id(relay_or_legacy_id, expected_type)
end
rescue InvalidIDError => e
GraphQL::ExecutionError.new(e.message)
end
end
def self.parse_relay_or_legacy_id(relay_or_legacy_id, expected_type)
if relay_or_legacy_id.nil? || relay_or_legacy_id =~ /\A\d+\Z/
relay_or_legacy_id
else
parse_relay_id(relay_or_legacy_id, expected_type)
end
end
def self.parse_relay_id(relay_id, expected_type)
type, id = GraphQL::Schema::UniqueWithinType.decode(relay_id)
if type != expected_type || id.nil?
raise InvalidIDError, "expected an id for #{expected_type}"
else
id
end
end
# TODO: move this into LockType after we switch to the class-based api
def self.make_lock_resolver(attr)
lambda do |lock, _, _|
if lock == false
nil
else
lock[attr]
end
end
end
class InvalidIDError < StandardError; end
endView on GitHub (pinned to 1c9f0bb801)